referrerpolicy=no-referrer-when-downgrade

collectives_westend_runtime/fellowship/
mod.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! The Westend Technical Fellowship.
18
19mod origins;
20mod tracks;
21use crate::{
22	weights,
23	xcm_config::{FellowshipAdminBodyId, LocationToAccountId, TreasurerBodyId, UsdtAssetHub},
24	AccountId, AssetRate, Balance, Balances, FellowshipReferenda, GovernanceLocation,
25	ParachainInfo, Preimage, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, Scheduler,
26	WestendTreasuryAccount, DAYS,
27};
28use cumulus_primitives_core::ParaId;
29use frame_support::{
30	parameter_types,
31	traits::{
32		tokens::UnityOrOuterConversion, EitherOf, EitherOfDiverse, FromContains, MapSuccess,
33		OriginTrait, TryWithMorphedArg,
34	},
35	PalletId,
36};
37use frame_system::{EnsureRoot, EnsureRootWithSuccess};
38pub use origins::{
39	pallet_origins as pallet_fellowship_origins, Architects, EnsureCanPromoteTo, EnsureCanRetainAt,
40	EnsureFellowship, Fellows, Masters, Members, ToVoice,
41};
42use pallet_ranked_collective::EnsureOfRank;
43use pallet_xcm::{EnsureXcm, IsVoiceOfBody};
44use parachains_common::impls::ToParentTreasury;
45use polkadot_runtime_common::impls::{
46	ContainsParts, LocatableAssetConverter, VersionedLocatableAsset, VersionedLocationConverter,
47};
48use sp_arithmetic::Permill;
49use sp_core::{ConstU128, ConstU32, ConstU8};
50use sp_runtime::traits::{ConstU16, ConvertToValue, IdentityLookup, Replace, TakeFirst};
51use testnet_parachains_constants::westend::{account, currency::GRAND};
52use westend_runtime_constants::time::HOURS;
53use xcm::prelude::*;
54use xcm_builder::{AliasesIntoAccountId32, PayOverXcm};
55
56#[cfg(feature = "runtime-benchmarks")]
57use crate::impls::benchmarks::{OpenHrmpChannel, PayWithEnsure};
58
59/// The Fellowship members' ranks.
60pub mod ranks {
61	use pallet_ranked_collective::Rank;
62
63	pub const DAN_1: Rank = 1; // aka Members.
64	pub const DAN_2: Rank = 2;
65	pub const DAN_3: Rank = 3; // aka Fellows.
66	pub const DAN_4: Rank = 4; // aka Architects.
67	pub const DAN_5: Rank = 5;
68	pub const DAN_6: Rank = 6;
69	pub const DAN_7: Rank = 7; // aka Masters.
70	pub const DAN_8: Rank = 8;
71	pub const DAN_9: Rank = 9;
72}
73
74impl pallet_fellowship_origins::Config for Runtime {}
75
76pub type FellowshipReferendaInstance = pallet_referenda::Instance1;
77
78impl pallet_referenda::Config<FellowshipReferendaInstance> for Runtime {
79	type WeightInfo = weights::pallet_referenda_fellowship_referenda::WeightInfo<Runtime>;
80	type RuntimeCall = RuntimeCall;
81	type RuntimeEvent = RuntimeEvent;
82	type Scheduler = Scheduler;
83	type Currency = Balances;
84	// Fellows can submit proposals.
85	type SubmitOrigin = EitherOf<
86		pallet_ranked_collective::EnsureMember<Runtime, FellowshipCollectiveInstance, 3>,
87		MapSuccess<
88			TryWithMorphedArg<
89				RuntimeOrigin,
90				<RuntimeOrigin as OriginTrait>::PalletsOrigin,
91				ToVoice,
92				EnsureOfRank<Runtime, FellowshipCollectiveInstance>,
93				(AccountId, u16),
94			>,
95			TakeFirst,
96		>,
97	>;
98	type CancelOrigin = Architects;
99	type KillOrigin = Masters;
100	type Slash = ToParentTreasury<WestendTreasuryAccount, LocationToAccountId, Runtime>;
101	type Votes = pallet_ranked_collective::Votes;
102	type Tally = pallet_ranked_collective::TallyOf<Runtime, FellowshipCollectiveInstance>;
103	type SubmissionDeposit = ConstU128<0>;
104	type MaxQueued = ConstU32<100>;
105	type UndecidingTimeout = ConstU32<{ 7 * DAYS }>;
106	type AlarmInterval = ConstU32<1>;
107	type Tracks = tracks::TracksInfo;
108	type Preimages = Preimage;
109	type BlockNumberProvider = crate::System;
110}
111
112pub type FellowshipCollectiveInstance = pallet_ranked_collective::Instance1;
113
114impl pallet_ranked_collective::Config<FellowshipCollectiveInstance> for Runtime {
115	type WeightInfo = weights::pallet_ranked_collective_fellowship_collective::WeightInfo<Runtime>;
116	type RuntimeEvent = RuntimeEvent;
117
118	// Promotions and the induction of new members are serviced by `FellowshipCore` pallet instance.
119	#[cfg(not(feature = "runtime-benchmarks"))]
120	type AddOrigin = frame_system::EnsureNever<()>;
121	#[cfg(feature = "runtime-benchmarks")]
122	type AddOrigin = frame_system::EnsureRoot<Self::AccountId>;
123
124	// The maximum value of `u16` set as a success value for the root to ensure the benchmarks will
125	// pass.
126	#[cfg(not(feature = "runtime-benchmarks"))]
127	type PromoteOrigin = frame_system::EnsureNever<pallet_ranked_collective::Rank>;
128	#[cfg(feature = "runtime-benchmarks")]
129	type PromoteOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
130
131	// Demotion is by any of:
132	// - Root can demote arbitrarily.
133	// - the FellowshipAdmin origin (i.e. token holder referendum);
134	//
135	// The maximum value of `u16` set as a success value for the root to ensure the benchmarks will
136	// pass.
137	type RemoveOrigin = Self::DemoteOrigin;
138	type DemoteOrigin = EitherOf<
139		EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>,
140		MapSuccess<
141			EnsureXcm<IsVoiceOfBody<GovernanceLocation, FellowshipAdminBodyId>>,
142			Replace<ConstU16<{ ranks::DAN_9 }>>,
143		>,
144	>;
145	// Exchange is by any of:
146	// - Root can exchange arbitrarily.
147	// - the Fellows origin
148	type ExchangeOrigin =
149		EitherOf<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, Fellows>;
150	type Polls = FellowshipReferenda;
151	type MinRankOfClass = tracks::MinRankOfClass;
152	type MemberSwappedHandler = (crate::FellowshipCore, crate::FellowshipSalary);
153	type VoteWeight = pallet_ranked_collective::Geometric;
154	type MaxMemberCount = ();
155	#[cfg(feature = "runtime-benchmarks")]
156	type BenchmarkSetup = (crate::FellowshipCore, crate::FellowshipSalary);
157}
158
159pub type FellowshipCoreInstance = pallet_core_fellowship::Instance1;
160
161impl pallet_core_fellowship::Config<FellowshipCoreInstance> for Runtime {
162	type WeightInfo = weights::pallet_core_fellowship_fellowship_core::WeightInfo<Runtime>;
163	type RuntimeEvent = RuntimeEvent;
164	type Members = pallet_ranked_collective::Pallet<Runtime, FellowshipCollectiveInstance>;
165	type Balance = Balance;
166	// Parameters are set by any of:
167	// - Root;
168	// - the FellowshipAdmin origin (i.e. token holder referendum);
169	// - a vote among all Fellows.
170	type ParamsOrigin = EitherOfDiverse<
171		EnsureXcm<IsVoiceOfBody<GovernanceLocation, FellowshipAdminBodyId>>,
172		Fellows,
173	>;
174	// Induction (creating a candidate) is by any of:
175	// - Root;
176	// - the FellowshipAdmin origin (i.e. token holder referendum);
177	// - a single Fellow;
178	// - a vote among all Members.
179	type InductOrigin = EitherOfDiverse<
180		EnsureXcm<IsVoiceOfBody<GovernanceLocation, FellowshipAdminBodyId>>,
181		EitherOfDiverse<
182			pallet_ranked_collective::EnsureMember<
183				Runtime,
184				FellowshipCollectiveInstance,
185				{ ranks::DAN_3 },
186			>,
187			Members,
188		>,
189	>;
190	// Approval (rank-retention) of a Member's current rank is by any of:
191	// - Root;
192	// - the FellowshipAdmin origin (i.e. token holder referendum);
193	// - a vote by the rank two above the current rank for all retention up to the Master rank.
194	type ApproveOrigin = EitherOf<
195		MapSuccess<
196			EnsureXcm<IsVoiceOfBody<GovernanceLocation, FellowshipAdminBodyId>>,
197			Replace<ConstU16<{ ranks::DAN_9 }>>,
198		>,
199		EnsureCanRetainAt,
200	>;
201	// Promotion is by any of:
202	// - Root can promote arbitrarily.
203	// - the FellowshipAdmin origin (i.e. token holder referendum);
204	// - a vote by the rank two above the new rank for all promotions up to the Master rank.
205	type PromoteOrigin = EitherOf<
206		MapSuccess<
207			EnsureXcm<IsVoiceOfBody<GovernanceLocation, FellowshipAdminBodyId>>,
208			Replace<ConstU16<{ ranks::DAN_9 }>>,
209		>,
210		EnsureCanPromoteTo,
211	>;
212	type FastPromoteOrigin = Self::PromoteOrigin;
213	type EvidenceSize = ConstU32<65536>;
214	type MaxRank = ConstU16<9>;
215}
216
217pub type FellowshipSalaryInstance = pallet_salary::Instance1;
218
219parameter_types! {
220	// The interior location on AssetHub for the paying account. This is the Fellowship Salary
221	// pallet instance (which sits at index 64). This sovereign account will need funding.
222	pub Interior: InteriorLocation = PalletInstance(64).into();
223}
224
225const USDT_UNITS: u128 = 1_000_000;
226
227/// [`PayOverXcm`] setup to pay the Fellowship salary on the AssetHub in USDT.
228pub type FellowshipSalaryPaymaster = PayOverXcm<
229	Interior,
230	crate::xcm_config::XcmRouter,
231	crate::PolkadotXcm,
232	ConstU32<{ 6 * HOURS }>,
233	AccountId,
234	(),
235	ConvertToValue<UsdtAssetHub>,
236	AliasesIntoAccountId32<(), AccountId>,
237>;
238
239impl pallet_salary::Config<FellowshipSalaryInstance> for Runtime {
240	type WeightInfo = weights::pallet_salary_fellowship_salary::WeightInfo<Runtime>;
241	type RuntimeEvent = RuntimeEvent;
242
243	#[cfg(not(feature = "runtime-benchmarks"))]
244	type Paymaster = FellowshipSalaryPaymaster;
245	#[cfg(feature = "runtime-benchmarks")]
246	type Paymaster = PayWithEnsure<FellowshipSalaryPaymaster, OpenHrmpChannel<ConstU32<1000>>>;
247	type Members = pallet_ranked_collective::Pallet<Runtime, FellowshipCollectiveInstance>;
248
249	#[cfg(not(feature = "runtime-benchmarks"))]
250	type Salary = pallet_core_fellowship::Pallet<Runtime, FellowshipCoreInstance>;
251	#[cfg(feature = "runtime-benchmarks")]
252	type Salary = frame_support::traits::tokens::ConvertRank<
253		crate::impls::benchmarks::RankToSalary<Balances>,
254	>;
255	// 15 days to register for a salary payment.
256	type RegistrationPeriod = ConstU32<{ 15 * DAYS }>;
257	// 15 days to claim the salary payment.
258	type PayoutPeriod = ConstU32<{ 15 * DAYS }>;
259	// Total monthly salary budget.
260	type Budget = ConstU128<{ 100_000 * USDT_UNITS }>;
261}
262
263parameter_types! {
264	pub const FellowshipTreasuryPalletId: PalletId = account::FELLOWSHIP_TREASURY_PALLET_ID;
265	pub const HundredPercent: Permill = Permill::from_percent(100);
266	pub const Burn: Permill = Permill::from_percent(0);
267	pub const MaxBalance: Balance = Balance::max_value();
268	// The asset's interior location for the paying account. This is the Fellowship Treasury
269	// pallet instance (which sits at index 65).
270	pub FellowshipTreasuryInteriorLocation: InteriorLocation = PalletInstance(65).into();
271	pub SelfParaId: ParaId = ParachainInfo::parachain_id();
272}
273
274/// [`PayOverXcm`] setup to pay the Fellowship Treasury.
275pub type FellowshipTreasuryPaymaster = PayOverXcm<
276	FellowshipTreasuryInteriorLocation,
277	crate::xcm_config::XcmRouter,
278	crate::PolkadotXcm,
279	ConstU32<{ 6 * HOURS }>,
280	VersionedLocation,
281	VersionedLocatableAsset,
282	LocatableAssetConverter,
283	VersionedLocationConverter,
284>;
285
286pub type FellowshipTreasuryInstance = pallet_treasury::Instance1;
287
288impl pallet_treasury::Config<FellowshipTreasuryInstance> for Runtime {
289	type WeightInfo = weights::pallet_treasury::WeightInfo<Runtime>;
290	type PalletId = FellowshipTreasuryPalletId;
291	type Currency = Balances;
292	type RejectOrigin = EitherOfDiverse<
293		EnsureRoot<AccountId>,
294		EitherOfDiverse<EnsureXcm<IsVoiceOfBody<GovernanceLocation, TreasurerBodyId>>, Fellows>,
295	>;
296	type RuntimeEvent = RuntimeEvent;
297	type SpendPeriod = ConstU32<{ 7 * DAYS }>;
298	type Burn = Burn;
299	type BurnDestination = ();
300	type SpendFunds = ();
301	type MaxApprovals = ConstU32<100>;
302	type SpendOrigin = EitherOf<
303		EitherOf<
304			EnsureRootWithSuccess<AccountId, MaxBalance>,
305			MapSuccess<
306				EnsureXcm<IsVoiceOfBody<GovernanceLocation, TreasurerBodyId>>,
307				Replace<ConstU128<{ 10_000 * GRAND }>>,
308			>,
309		>,
310		EitherOf<
311			MapSuccess<Architects, Replace<ConstU128<{ 10_000 * GRAND }>>>,
312			MapSuccess<Fellows, Replace<ConstU128<{ 10 * GRAND }>>>,
313		>,
314	>;
315	type AssetKind = VersionedLocatableAsset;
316	type Beneficiary = VersionedLocation;
317	type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
318	#[cfg(not(feature = "runtime-benchmarks"))]
319	type Paymaster = FellowshipTreasuryPaymaster;
320	#[cfg(feature = "runtime-benchmarks")]
321	type Paymaster = PayWithEnsure<FellowshipTreasuryPaymaster, OpenHrmpChannel<ConstU32<1000>>>;
322	type BalanceConverter = UnityOrOuterConversion<
323		ContainsParts<
324			FromContains<
325				xcm_builder::IsSiblingSystemParachain<ParaId, SelfParaId>,
326				xcm_builder::IsParentsOnly<ConstU8<1>>,
327			>,
328		>,
329		AssetRate,
330	>;
331	type PayoutPeriod = ConstU32<{ 30 * DAYS }>;
332	#[cfg(feature = "runtime-benchmarks")]
333	type BenchmarkHelper = polkadot_runtime_common::impls::benchmarks::TreasuryArguments<
334		sp_core::ConstU8<1>,
335		ConstU32<1000>,
336	>;
337	type BlockNumberProvider = crate::System;
338}