diff --git a/CHANGELOG.md b/CHANGELOG.md index c9e9397901..b55f304557 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Migration to remove all but the 21 first elected Head Ambassador members from the Program ([polkadot-fellows/runtimes#422](https://github.com/polkadot-fellows/runtimes/pull/422)). - Kusama: Make the current inflation formula adjustable ([polkadot-fellows/runtimes#364](https://github.com/polkadot-fellows/runtimes/pull/364)) - Port Agile Coretime migration from polkadot-sdk in order to fix leases with gaps handling([polkadot-fellows/runtimes#426](https://github.com/polkadot-fellows/runtimes/pull/426)) +- The Secretary Program ([polkadot-fellows/runtimes#306](https://github.com/polkadot-fellows/runtimes/pull/306)) #### From [#322](https://github.com/polkadot-fellows/runtimes/pull/322): @@ -410,4 +411,4 @@ Note: This release only affects the following runtimes and is not a full system - Assets `destroy_accounts` releases the deposit ([paritytech/substrate#14443](https://github.com/paritytech/substrate/pull/14443)) - Update Polkadot Collectives to use `limited_teleport_assets` for automatic slash handling, as - `teleport_assets` is deprecated and caused a failing integration test. ([polkadot-fellows/runtimes#46](https://github.com/polkadot-fellows/runtimes/pull/46)) + `teleport_assets` is deprecated and caused a failing integration test. ([polkadot-fellows/runtimes#46](https://github.com/polkadot-fellows/runtimes/pull/46)) \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a34e028c5d..2caeae69d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7415,6 +7415,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-format" version = "0.4.4" @@ -15020,12 +15026,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -15040,10 +15047,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/impls.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/impls.rs index 68a3bfb318..049004706c 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/impls.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/impls.rs @@ -267,8 +267,9 @@ pub mod tx_payment { // The error should not occur since swap was quoted before. Err((refund, _)) => { match T::Assets::settle(who, debt, Preservation::Expendable) { - Ok(dust) => - ensure!(dust.peek().is_zero(), InvalidTransaction::Payment), + Ok(dust) => { + ensure!(dust.peek().is_zero(), InvalidTransaction::Payment) + }, // The error should not occur as the `debt` was just withdrawn // above. Err(_) => return Err(InvalidTransaction::Payment.into()), diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs index 67522fcd60..e84386623a 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs @@ -485,15 +485,14 @@ impl InstanceFilter for ProxyType { RuntimeCall::Utility { .. } | RuntimeCall::Multisig { .. } ), - ProxyType::Assets => { - matches!( - c, - RuntimeCall::Assets { .. } | - RuntimeCall::Utility { .. } | - RuntimeCall::Multisig { .. } | - RuntimeCall::Nfts { .. } | RuntimeCall::Uniques { .. } - ) - }, + ProxyType::Assets => matches!( + c, + RuntimeCall::Assets { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } | + RuntimeCall::Nfts { .. } | + RuntimeCall::Uniques { .. } + ), ProxyType::AssetOwner => matches!( c, RuntimeCall::Assets(TrustBackedAssetsCall::create { .. }) | diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/xcm_config.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/xcm_config.rs index 66fbf44c88..a0ff8b7a2a 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/xcm_config.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/xcm_config.rs @@ -227,6 +227,8 @@ parameter_types! { pub XcmAssetFeesReceiver: Option = Authorship::author(); } +/// Location type to determine the Technical Fellowship related +/// pallets for use in XCM. pub struct FellowshipEntities; impl Contains for FellowshipEntities { fn contains(location: &Location) -> bool { @@ -259,6 +261,8 @@ impl Contains for FellowshipEntities { } } +/// Location type to determine the Ambassador Collective +/// pallets for use in XCM. pub struct AmbassadorEntities; impl Contains for AmbassadorEntities { fn contains(location: &Location) -> bool { @@ -285,6 +289,26 @@ impl Contains for AmbassadorEntities { } } +/// Location type to determine the Secretary Collective related +/// pallets for use in XCM. +pub struct SecretaryEntities; +impl Contains for SecretaryEntities { + fn contains(location: &Location) -> bool { + matches!( + location.unpack(), + ( + 1, + [ + Parachain(system_parachain::COLLECTIVES_ID), + PalletInstance( + collectives_polkadot_runtime_constants::SECRETARY_SALARY_PALLET_INDEX + ) + ] + ) + ) + } +} + pub struct ParentOrParentsPlurality; impl Contains for ParentOrParentsPlurality { fn contains(location: &Location) -> bool { @@ -314,6 +338,7 @@ pub type Barrier = TrailingSetTopicAsId< Equals, Equals, AmbassadorEntities, + SecretaryEntities, )>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, @@ -340,6 +365,7 @@ pub type WaivedLocations = ( Equals, FellowshipEntities, AmbassadorEntities, + SecretaryEntities, ); /// Cases where a remote origin is accepted as trusted Teleporter for a given asset: diff --git a/system-parachains/collectives/collectives-polkadot/constants/src/lib.rs b/system-parachains/collectives/collectives-polkadot/constants/src/lib.rs index 7fe81f8019..155366b22d 100644 --- a/system-parachains/collectives/collectives-polkadot/constants/src/lib.rs +++ b/system-parachains/collectives/collectives-polkadot/constants/src/lib.rs @@ -27,3 +27,6 @@ pub const AMBASSADOR_SALARY_PALLET_INDEX: u8 = 74; /// Polkadot Ambassador Treasury pallet instance. pub const AMBASSADOR_TREASURY_PALLET_INDEX: u8 = 75; + +/// Polkadot Secretary Salary pallet instance. +pub const SECRETARY_SALARY_PALLET_INDEX: u8 = 81; diff --git a/system-parachains/collectives/collectives-polkadot/src/lib.rs b/system-parachains/collectives/collectives-polkadot/src/lib.rs index 0ddd613f0c..42f26b43fb 100644 --- a/system-parachains/collectives/collectives-polkadot/src/lib.rs +++ b/system-parachains/collectives/collectives-polkadot/src/lib.rs @@ -46,6 +46,9 @@ pub mod xcm_config; pub mod fellowship; pub use ambassador::pallet_ambassador_origins; +// Secretary Configuration +pub mod secretary; + use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use fellowship::{pallet_fellowship_origins, Fellows}; @@ -314,6 +317,8 @@ pub enum ProxyType { Fellowship, /// Ambassador proxy. Allows calls related to the Ambassador Program. Ambassador, + /// Secretary proxy. Allows calls related to the Secretary collective + Secretary, } impl Default for ProxyType { fn default() -> Self { @@ -360,6 +365,13 @@ impl InstanceFilter for ProxyType { RuntimeCall::Utility { .. } | RuntimeCall::Multisig { .. } ), + ProxyType::Secretary => matches!( + c, + RuntimeCall::SecretaryCollective { .. } | + RuntimeCall::SecretarySalary { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), } } fn is_superset(&self, o: &Self) -> bool { @@ -740,6 +752,12 @@ construct_runtime!( AmbassadorCore: pallet_core_fellowship:: = 73, AmbassadorSalary: pallet_salary:: = 74, AmbassadorTreasury: pallet_treasury:: = 75, + + // The Secretary Collective + // pub type SecretaryCollectiveInstance = pallet_ranked_collective::instance3; + SecretaryCollective: pallet_ranked_collective:: = 80, + // pub type SecretarySalaryInstance = pallet_salary::Instance3; + SecretarySalary: pallet_salary:: = 81, } ); @@ -819,6 +837,8 @@ mod benches { [pallet_core_fellowship, AmbassadorCore] [pallet_salary, AmbassadorSalary] [pallet_treasury, AmbassadorTreasury] + [pallet_ranked_collective, SecretaryCollective] + [pallet_salary, SecretarySalary] ); } diff --git a/system-parachains/collectives/collectives-polkadot/src/secretary/mod.rs b/system-parachains/collectives/collectives-polkadot/src/secretary/mod.rs new file mode 100644 index 0000000000..1d2f36aaf9 --- /dev/null +++ b/system-parachains/collectives/collectives-polkadot/src/secretary/mod.rs @@ -0,0 +1,195 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! The Polkadot Secretary Collective. + +use core::marker::PhantomData; + +use crate::{fellowship::FellowshipAdminBodyId, *}; +use frame_support::{ + parameter_types, + traits::{tokens::GetSalary, EitherOf, MapSuccess, PalletInfoAccess, PollStatus, Polling}, +}; +use frame_system::{pallet_prelude::BlockNumberFor, EnsureRootWithSuccess}; +use pallet_ranked_collective::{MemberIndex, TallyOf, Votes}; +use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; +use polkadot_runtime_constants::time::HOURS; +use sp_core::{ConstU128, ConstU32}; +use sp_runtime::{ + traits::{ConstU16, ConvertToValue, Identity, Replace}, + DispatchError, +}; + +use xcm::prelude::*; +use xcm_builder::{AliasesIntoAccountId32, PayOverXcm}; + +use self::xcm_config::AssetHubUsdt; + +/// The Secretary members' ranks. +pub mod ranks { + use pallet_ranked_collective::Rank; + + pub const SECRETARY_CANDIDATE: Rank = 0; + pub const SECRETARY: Rank = 1; +} + +/// Origins of: +/// - Root; +/// - FellowshipAdmin (i.e. token holder referendum); +/// - Plurality vote from Fellows can promote, demote, remove and approve rank retention +/// of members of the Secretary Collective (rank `2`). +type ApproveOrigin = EitherOf< + EnsureRootWithSuccess>, + EitherOf< + MapSuccess< + EnsureXcm>, + Replace>, + >, + MapSuccess>>, + >, +>; + +pub struct SecretaryPolling, I: 'static>( + PhantomData<(T, I)>, +); + +// TODO: Include no-op impl in the Polkadot-SDK(https://github.com/paritytech/polkadot-sdk/pull/5311) +impl, I: 'static> Polling> + for SecretaryPolling +{ + type Index = MemberIndex; + type Votes = Votes; + type Class = u16; + type Moment = BlockNumberFor; + + fn classes() -> Vec { + vec![] + } + + fn as_ongoing(_index: Self::Index) -> Option<(TallyOf, Self::Class)> { + None + } + + fn access_poll( + _index: Self::Index, + f: impl FnOnce(PollStatus<&mut TallyOf, Self::Moment, Self::Class>) -> R, + ) -> R { + f(PollStatus::None) + } + + fn try_access_poll( + _index: Self::Index, + f: impl FnOnce( + PollStatus<&mut TallyOf, Self::Moment, Self::Class>, + ) -> Result, + ) -> Result { + f(PollStatus::None) + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_ongoing(_class: Self::Class) -> Result { + Err(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn end_ongoing(_index: Self::Index, _approved: bool) -> Result<(), ()> { + Err(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn max_ongoing() -> (Self::Class, u32) { + (0, 0) + } +} + +pub type SecretaryCollectiveInstance = pallet_ranked_collective::Instance3; + +impl pallet_ranked_collective::Config for Runtime { + type WeightInfo = (); // TODO weights::pallet_ranked_collective_secretary_collective::WeightInfo; + type RuntimeEvent = RuntimeEvent; + type AddOrigin = ApproveOrigin; + type RemoveOrigin = ApproveOrigin; + type PromoteOrigin = ApproveOrigin; + type DemoteOrigin = ApproveOrigin; + type ExchangeOrigin = ApproveOrigin; + type Polls = SecretaryPolling; + type MinRankOfClass = Identity; + type MemberSwappedHandler = crate::SecretarySalary; + type VoteWeight = pallet_ranked_collective::Geometric; + type MaxMemberCount = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup = crate::SecretarySalary; +} + +pub type SecretarySalaryInstance = pallet_salary::Instance3; + +parameter_types! { + // The interior location on AssetHub for the paying account. This is the Secretary Salary + // pallet instance. This sovereign account will need funding. + pub SecretarySalaryInteriorLocation: InteriorLocation = PalletInstance(::index() as u8).into(); +} + +const USDT_UNITS: u128 = 1_000_000; + +/// [`PayOverXcm`] setup to pay the Secretary salary on the AssetHub in USDT. +pub type SecretarySalaryPaymaster = PayOverXcm< + SecretarySalaryInteriorLocation, + crate::xcm_config::XcmRouter, + crate::PolkadotXcm, + ConstU32<{ 6 * HOURS }>, + AccountId, + (), + ConvertToValue, + AliasesIntoAccountId32<(), AccountId>, +>; + +pub struct SalaryForRank; +impl GetSalary for SalaryForRank { + fn get_salary(rank: u16, _who: &AccountId) -> Balance { + if rank == 1 { + 6666 * USDT_UNITS + } else { + 0 + } + } +} + +impl pallet_salary::Config for Runtime { + type WeightInfo = (); // TODO weights::pallet_salary_secretary_salary::WeightInfo; + type RuntimeEvent = RuntimeEvent; + + #[cfg(not(feature = "runtime-benchmarks"))] + type Paymaster = SecretarySalaryPaymaster; + #[cfg(feature = "runtime-benchmarks")] + type Paymaster = crate::impls::benchmarks::PayWithEnsure< + SecretarySalaryPaymaster, + crate::impls::benchmarks::OpenHrmpChannel>, + >; + type Members = pallet_ranked_collective::Pallet; + + #[cfg(not(feature = "runtime-benchmarks"))] + type Salary = SalaryForRank; + #[cfg(feature = "runtime-benchmarks")] + type Salary = frame_support::traits::tokens::ConvertRank< + crate::impls::benchmarks::RankToSalary, + >; + // 15 days to register for a salary payment. + type RegistrationPeriod = ConstU32<{ 15 * DAYS }>; + // 15 days to claim the salary payment. + type PayoutPeriod = ConstU32<{ 15 * DAYS }>; + // Total monthly salary budget. + type Budget = ConstU128<{ 6666 * USDT_UNITS }>; +}