referrerpolicy=no-referrer-when-downgrade

collectives_westend_runtime/ambassador/
mod.rs

1// Copyright (C) 2022 Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! The Ambassador Program.
17//!
18//! The module defines the following on-chain functionality of the Ambassador Program:
19//!
20//! - Managed set of program members, where every member has a [rank](ranks)
21//! (via [AmbassadorCollective](pallet_ranked_collective)).
22//! - Referendum functionality for the program members to propose, vote on, and execute
23//! proposals on behalf of the members of a certain [rank](Origin)
24//! (via [AmbassadorReferenda](pallet_referenda)).
25//! - Managed content (charter, announcements) (via [pallet_collective_content]).
26//! - Promotion and demotion periods, register of members' activity, and rank based salaries
27//! (via [AmbassadorCore](pallet_core_fellowship)).
28//! - Members' salaries (via [AmbassadorSalary](pallet_salary), requiring a member to be
29//! imported or inducted into [AmbassadorCore](pallet_core_fellowship)).
30
31pub mod origins;
32mod tracks;
33
34use super::*;
35use crate::xcm_config::{FellowshipAdminBodyId, LocationToAccountId, WndAssetHub};
36use frame_support::traits::{EitherOf, MapSuccess, TryMapSuccess};
37use frame_system::EnsureRootWithSuccess;
38pub use origins::pallet_origins as pallet_ambassador_origins;
39use origins::pallet_origins::{
40	EnsureAmbassadorsVoice, EnsureAmbassadorsVoiceFrom, EnsureHeadAmbassadorsVoice, Origin,
41};
42use sp_core::ConstU128;
43use sp_runtime::traits::{CheckedReduceBy, ConstU16, ConvertToValue, Replace, ReplaceWithDefault};
44use xcm::prelude::*;
45use xcm_builder::{AliasesIntoAccountId32, PayOverXcm};
46
47/// The Ambassador Program's member ranks.
48pub mod ranks {
49	use pallet_ranked_collective::Rank;
50
51	#[allow(dead_code)]
52	pub const CANDIDATE: Rank = 0;
53	pub const AMBASSADOR_TIER_1: Rank = 1;
54	pub const AMBASSADOR_TIER_2: Rank = 2;
55	pub const SENIOR_AMBASSADOR_TIER_3: Rank = 3;
56	pub const SENIOR_AMBASSADOR_TIER_4: Rank = 4;
57	pub const HEAD_AMBASSADOR_TIER_5: Rank = 5;
58	pub const HEAD_AMBASSADOR_TIER_6: Rank = 6;
59	pub const HEAD_AMBASSADOR_TIER_7: Rank = 7;
60	pub const MASTER_AMBASSADOR_TIER_8: Rank = 8;
61	pub const MASTER_AMBASSADOR_TIER_9: Rank = 9;
62}
63
64impl pallet_ambassador_origins::Config for Runtime {}
65
66pub type AmbassadorCollectiveInstance = pallet_ranked_collective::Instance2;
67
68/// Demotion is by any of:
69/// - Root can demote arbitrarily.
70/// - the FellowshipAdmin origin (i.e. token holder referendum);
71/// - a senior members vote by the rank two above the current rank.
72pub type DemoteOrigin = EitherOf<
73	frame_system::EnsureRootWithSuccess<AccountId, ConstU16<65535>>,
74	EitherOf<
75		MapSuccess<
76			EnsureXcm<IsVoiceOfBody<GovernanceLocation, FellowshipAdminBodyId>>,
77			Replace<ConstU16<{ ranks::MASTER_AMBASSADOR_TIER_9 }>>,
78		>,
79		TryMapSuccess<
80			EnsureAmbassadorsVoiceFrom<ConstU16<{ ranks::SENIOR_AMBASSADOR_TIER_3 }>>,
81			CheckedReduceBy<ConstU16<2>>,
82		>,
83	>,
84>;
85
86/// Promotion and approval (rank-retention) is by any of:
87/// - Root can promote arbitrarily.
88/// - the FellowshipAdmin origin (i.e. token holder referendum);
89/// - a senior members vote by the rank two above the new/current rank.
90/// - a member of rank `5` or above can add a candidate (rank `0`).
91pub type PromoteOrigin = EitherOf<
92	DemoteOrigin,
93	TryMapSuccess<
94		pallet_ranked_collective::EnsureMember<
95			Runtime,
96			AmbassadorCollectiveInstance,
97			{ ranks::HEAD_AMBASSADOR_TIER_5 },
98		>,
99		Replace<ConstU16<0>>,
100	>,
101>;
102
103/// Exchange is by any of:
104/// - Root can exchange arbitrarily.
105/// - the Fellows origin
106pub type ExchangeOrigin = EitherOf<EnsureRootWithSuccess<AccountId, ConstU16<65535>>, Fellows>;
107
108impl pallet_ranked_collective::Config<AmbassadorCollectiveInstance> for Runtime {
109	type WeightInfo = weights::pallet_ranked_collective_ambassador_collective::WeightInfo<Runtime>;
110	type RuntimeEvent = RuntimeEvent;
111	type AddOrigin = MapSuccess<Self::PromoteOrigin, ReplaceWithDefault<()>>;
112	type PromoteOrigin = PromoteOrigin;
113	type DemoteOrigin = DemoteOrigin;
114	type RemoveOrigin = Self::DemoteOrigin;
115	type ExchangeOrigin = ExchangeOrigin;
116	type Polls = AmbassadorReferenda;
117	type MinRankOfClass = sp_runtime::traits::Identity;
118	type MemberSwappedHandler = (crate::AmbassadorCore, crate::AmbassadorSalary);
119	type VoteWeight = pallet_ranked_collective::Linear;
120	type MaxMemberCount = ();
121	#[cfg(feature = "runtime-benchmarks")]
122	type BenchmarkSetup = (crate::AmbassadorCore, crate::AmbassadorSalary);
123}
124
125parameter_types! {
126	pub const AlarmInterval: BlockNumber = 1;
127	pub const SubmissionDeposit: Balance = 0;
128	pub const UndecidingTimeout: BlockNumber = 7 * DAYS;
129}
130
131pub type AmbassadorReferendaInstance = pallet_referenda::Instance2;
132
133impl pallet_referenda::Config<AmbassadorReferendaInstance> for Runtime {
134	type WeightInfo = weights::pallet_referenda_ambassador_referenda::WeightInfo<Runtime>;
135	type RuntimeCall = RuntimeCall;
136	type RuntimeEvent = RuntimeEvent;
137	type Scheduler = Scheduler;
138	type Currency = Balances;
139	// A proposal can be submitted by a member of the Ambassador Program of
140	// [ranks::SENIOR_AMBASSADOR_TIER_3] rank or higher.
141	type SubmitOrigin = pallet_ranked_collective::EnsureMember<
142		Runtime,
143		AmbassadorCollectiveInstance,
144		{ ranks::SENIOR_AMBASSADOR_TIER_3 },
145	>;
146	type CancelOrigin = EitherOf<EnsureRoot<AccountId>, EnsureHeadAmbassadorsVoice>;
147	type KillOrigin = EitherOf<EnsureRoot<AccountId>, EnsureHeadAmbassadorsVoice>;
148	type Slash = ToParentTreasury<WestendTreasuryAccount, LocationToAccountId, Runtime>;
149	type Votes = pallet_ranked_collective::Votes;
150	type Tally = pallet_ranked_collective::TallyOf<Runtime, AmbassadorCollectiveInstance>;
151	type SubmissionDeposit = SubmissionDeposit;
152	type MaxQueued = ConstU32<20>;
153	type UndecidingTimeout = UndecidingTimeout;
154	type AlarmInterval = AlarmInterval;
155	type Tracks = tracks::TracksInfo;
156	type Preimages = Preimage;
157	type BlockNumberProvider = System;
158}
159
160parameter_types! {
161	pub const AnnouncementLifetime: BlockNumber = 180 * DAYS;
162	pub const MaxAnnouncements: u32 = 50;
163}
164
165pub type AmbassadorContentInstance = pallet_collective_content::Instance1;
166
167impl pallet_collective_content::Config<AmbassadorContentInstance> for Runtime {
168	type RuntimeEvent = RuntimeEvent;
169	type CharterOrigin = EitherOf<EnsureRoot<AccountId>, EnsureHeadAmbassadorsVoice>;
170	type AnnouncementLifetime = AnnouncementLifetime;
171	// An announcement can be submitted by a Senior Ambassador member or an ambassador plurality
172	// voice taken via referendum.
173	type AnnouncementOrigin = EitherOfDiverse<
174		pallet_ranked_collective::EnsureMember<
175			Runtime,
176			AmbassadorCollectiveInstance,
177			{ ranks::SENIOR_AMBASSADOR_TIER_3 },
178		>,
179		EnsureAmbassadorsVoice,
180	>;
181	type MaxAnnouncements = MaxAnnouncements;
182	type WeightInfo = weights::pallet_collective_content::WeightInfo<Runtime>;
183}
184
185pub type AmbassadorCoreInstance = pallet_core_fellowship::Instance2;
186
187impl pallet_core_fellowship::Config<AmbassadorCoreInstance> for Runtime {
188	type WeightInfo = weights::pallet_core_fellowship_ambassador_core::WeightInfo<Runtime>;
189	type RuntimeEvent = RuntimeEvent;
190	type Members = pallet_ranked_collective::Pallet<Runtime, AmbassadorCollectiveInstance>;
191	type Balance = Balance;
192	// Parameters are set by any of:
193	// - Root;
194	// - the FellowshipAdmin origin (i.e. token holder referendum);
195	// - a vote among all Head Ambassadors.
196	type ParamsOrigin = EitherOfDiverse<
197		EnsureRoot<AccountId>,
198		EitherOfDiverse<
199			EnsureXcm<IsVoiceOfBody<GovernanceLocation, FellowshipAdminBodyId>>,
200			EnsureHeadAmbassadorsVoice,
201		>,
202	>;
203	// Induction (creating a candidate) is by any of:
204	// - Root;
205	// - the FellowshipAdmin origin (i.e. token holder referendum);
206	// - a single Head Ambassador;
207	// - a vote among all senior members.
208	type InductOrigin = EitherOfDiverse<
209		EnsureRoot<AccountId>,
210		EitherOfDiverse<
211			EnsureXcm<IsVoiceOfBody<GovernanceLocation, FellowshipAdminBodyId>>,
212			EitherOfDiverse<
213				pallet_ranked_collective::EnsureMember<
214					Runtime,
215					AmbassadorCollectiveInstance,
216					{ ranks::HEAD_AMBASSADOR_TIER_5 },
217				>,
218				EnsureAmbassadorsVoiceFrom<ConstU16<{ ranks::SENIOR_AMBASSADOR_TIER_3 }>>,
219			>,
220		>,
221	>;
222	type ApproveOrigin = PromoteOrigin;
223	type PromoteOrigin = PromoteOrigin;
224	type FastPromoteOrigin = Self::PromoteOrigin;
225	type EvidenceSize = ConstU32<65536>;
226	type MaxRank = ConstU16<9>;
227}
228
229pub type AmbassadorSalaryInstance = pallet_salary::Instance2;
230
231parameter_types! {
232	// The interior location on AssetHub for the paying account. This is the Ambassador Salary
233	// pallet instance (which sits at index 74). This sovereign account will need funding.
234	pub AmbassadorSalaryLocation: InteriorLocation = PalletInstance(74).into();
235}
236
237/// [`PayOverXcm`] setup to pay the Ambassador salary on the AssetHub in WND.
238pub type AmbassadorSalaryPaymaster = PayOverXcm<
239	AmbassadorSalaryLocation,
240	crate::xcm_config::XcmRouter,
241	crate::PolkadotXcm,
242	ConstU32<{ 6 * HOURS }>,
243	AccountId,
244	(),
245	ConvertToValue<WndAssetHub>,
246	AliasesIntoAccountId32<(), AccountId>,
247>;
248
249impl pallet_salary::Config<AmbassadorSalaryInstance> for Runtime {
250	type WeightInfo = weights::pallet_salary_ambassador_salary::WeightInfo<Runtime>;
251	type RuntimeEvent = RuntimeEvent;
252
253	#[cfg(not(feature = "runtime-benchmarks"))]
254	type Paymaster = AmbassadorSalaryPaymaster;
255	#[cfg(feature = "runtime-benchmarks")]
256	type Paymaster = crate::impls::benchmarks::PayWithEnsure<
257		AmbassadorSalaryPaymaster,
258		crate::impls::benchmarks::OpenHrmpChannel<ConstU32<1000>>,
259	>;
260	type Members = pallet_ranked_collective::Pallet<Runtime, AmbassadorCollectiveInstance>;
261
262	#[cfg(not(feature = "runtime-benchmarks"))]
263	type Salary = pallet_core_fellowship::Pallet<Runtime, AmbassadorCoreInstance>;
264	#[cfg(feature = "runtime-benchmarks")]
265	type Salary = frame_support::traits::tokens::ConvertRank<
266		crate::impls::benchmarks::RankToSalary<Balances>,
267	>;
268	// 15 days to register for a salary payment.
269	type RegistrationPeriod = ConstU32<{ 15 * DAYS }>;
270	// 15 days to claim the salary payment.
271	type PayoutPeriod = ConstU32<{ 15 * DAYS }>;
272	// Total monthly salary budget.
273	type Budget = ConstU128<{ 10_000 * DOLLARS }>;
274}