Skip to content

Commit

Permalink
Fellowship Treasury (#109)
Browse files Browse the repository at this point in the history
Treasury Pallet Instance for the Fellowship in Polkadot Collectives.

In this update, we present a Treasury Pallet Instance that is under the
control of the Fellowship body, with oversight from the Root and
Treasurer origins. Here's how it is governed:
- the Root origin have the authority to reject or approve spend
proposals, with no amount limit for approvals.
- the Treasurer origin have the authority to reject or approve spend
proposals, with approval limits of up to 10,000,000 DOT.
- Voice of all Fellows ranked at 3 or above can reject or approve spend
proposals, with a maximum approval limit of 10,000 DOT.
- Voice of Fellows ranked at 4 or above can also reject or approve spend
proposals, with a maximum approval limit of 10,000,000 DOT.

Additionally, we introduce the Asset Rate Pallet Instance to establish
conversion rates from asset A to B. This is used to determine if a
proposed spend amount involving a non-native asset is permissible by the
commanding origin. The rates can be set up by the Root, Treasurer
origins, or Voice of all Fellows.

test with xcm-emulator for the same setup in Westend -
paritytech/polkadot-sdk#2532
more details on new treasury features and asset rate pallet -
paritytech/polkadot-sdk#1333

---------

Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: joepetrowski <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: Oliver Tale-Yazdi <[email protected]>
  • Loading branch information
5 people authored Dec 8, 2023
1 parent d67b905 commit 49a04e5
Show file tree
Hide file tree
Showing 12 changed files with 515 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- XCM transport fees are now exponential and are sent to a treasury account ([polkadot-fellows/runtimes#87](https://github.com/polkadot-fellows/runtimes/pull/87)). Context: https://github.com/paritytech/polkadot-sdk/pull/1234
- System parachains are now trusted teleporters of each other ([polkadot-fellows/runtimes#87](https://github.com/polkadot-fellows/runtimes/pull/87)). Context: https://github.com/paritytech/polkadot-sdk/pull/1368
- Treasury is able to spend various asset kinds ([polkadot-fellows/runtimes#87](https://github.com/polkadot-fellows/runtimes/pull/87))
- Fellowship Treasury pallet on Polkadot Collectives ([polkadot-fellows/runtimes#109](https://github.com/polkadot-fellows/runtimes/pull/109))

### Fixed

Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions relay/polkadot/constants/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ pub mod xcm {
const ROOT_INDEX: u32 = 0;
// The bodies corresponding to the Polkadot OpenGov Origins.
pub const FELLOWSHIP_ADMIN_INDEX: u32 = 1;
// The body corresponding to the Treasurer OpenGov track.
pub const TREASURER_INDEX: u32 = 2;
}
}

Expand Down
13 changes: 11 additions & 2 deletions relay/polkadot/src/xcm_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use super::{
parachains_origin, AccountId, AllPalletsWithSystem, Balances, Dmp, FellowshipAdmin,
GeneralAdmin, ParaId, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin,
TransactionByteFee, Treasury, WeightToFee, XcmPallet,
TransactionByteFee, Treasurer, Treasury, WeightToFee, XcmPallet,
};
use frame_support::{
match_types, parameter_types,
Expand All @@ -29,7 +29,9 @@ use frame_support::{
use frame_system::EnsureRoot;
use pallet_xcm::XcmPassthrough;
use polkadot_runtime_constants::{
currency::CENTS, system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX,
currency::CENTS,
system_parachain::*,
xcm::body::{FELLOWSHIP_ADMIN_INDEX, TREASURER_INDEX},
};
use runtime_common::{
crowdloan, paras_registrar,
Expand Down Expand Up @@ -362,6 +364,8 @@ parameter_types! {
pub const StakingAdminBodyId: BodyId = BodyId::Defense;
// FellowshipAdmin pluralistic body.
pub const FellowshipAdminBodyId: BodyId = BodyId::Index(FELLOWSHIP_ADMIN_INDEX);
// `Treasurer` pluralistic body.
pub const TreasurerBodyId: BodyId = BodyId::Index(TREASURER_INDEX);
}

#[cfg(feature = "runtime-benchmarks")]
Expand Down Expand Up @@ -389,6 +393,9 @@ pub type StakingAdminToPlurality =
pub type FellowshipAdminToPlurality =
OriginToPluralityVoice<RuntimeOrigin, FellowshipAdmin, FellowshipAdminBodyId>;

/// Type to convert the `Treasurer` origin to a Plurality `MultiLocation` value.
pub type TreasurerToPlurality = OriginToPluralityVoice<RuntimeOrigin, Treasurer, TreasurerBodyId>;

/// Type to convert a pallet `Origin` type value into a `MultiLocation` value which represents an
/// interior location of this chain for a destination chain.
pub type LocalPalletOriginToLocation = (
Expand All @@ -398,6 +405,8 @@ pub type LocalPalletOriginToLocation = (
StakingAdminToPlurality,
// FellowshipAdmin origin to be used in XCM as a corresponding Plurality `MultiLocation` value.
FellowshipAdminToPlurality,
// `Treasurer` origin to be used in XCM as a corresponding Plurality `MultiLocation` value.
TreasurerToPlurality,
);

impl pallet_xcm::Config for Runtime {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,13 @@ match_types! {
MultiLocation { parents: 1, interior: Here } |
MultiLocation { parents: 1, interior: X1(_) }
};
pub type FellowsPlurality: impl Contains<MultiLocation> = {
MultiLocation { parents: 1, interior: X2(Parachain(1001), Plurality { id: BodyId::Technical, ..}) }
};
pub type FellowshipSalaryPallet: impl Contains<MultiLocation> = {
MultiLocation { parents: 1, interior: X2(Parachain(1001), PalletInstance(64)) }
pub type FellowshipEntities: impl Contains<MultiLocation> = {
// Fellowship Plurality
MultiLocation { parents: 1, interior: X2(Parachain(1001), Plurality { id: BodyId::Technical, ..}) } |
// Fellowship Salary Pallet
MultiLocation { parents: 1, interior: X2(Parachain(1001), PalletInstance(64)) } |
// Fellowship Treasury Pallet
MultiLocation { parents: 1, interior: X2(Parachain(1001), PalletInstance(65)) }
};
}

Expand Down Expand Up @@ -380,8 +382,7 @@ pub type Barrier = TrailingSetTopicAsId<
// The locations listed below get free execution.
AllowExplicitUnpaidExecutionFrom<(
ParentOrParentsPlurality,
FellowsPlurality,
FellowshipSalaryPallet,
FellowshipEntities,
Equals<RelayTreasuryLocation>,
)>,
// Subscriptions for version tracking are OK.
Expand Down Expand Up @@ -419,8 +420,7 @@ match_types! {
pub type WaivedLocations = (
RelayOrOtherSystemParachains<SystemParachains, Runtime>,
Equals<RelayTreasuryLocation>,
FellowsPlurality,
FellowshipSalaryPallet,
FellowshipEntities,
);

/// Cases where a remote origin is accepted as trusted Teleporter for a given asset:
Expand Down
8 changes: 8 additions & 0 deletions system-parachains/collectives/collectives-polkadot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ frame-system = { default-features = false, version = "25.0.0" }
frame-system-benchmarking = { default-features = false, optional = true, version = "25.0.0" }
frame-system-rpc-runtime-api = { default-features = false, version = "23.0.0" }
frame-try-runtime = { default-features = false, optional = true, version = "0.31.0" }
pallet-asset-rate = { default-features = false , version = "4.0.0" }
pallet-alliance = { default-features = false, version = "24.0.0" }
pallet-aura = { default-features = false, version = "24.0.0" }
pallet-authorship = { default-features = false, version = "25.0.0" }
Expand All @@ -36,6 +37,7 @@ pallet-session = { default-features = false, version = "25.0.0" }
pallet-timestamp = { default-features = false, version = "24.0.0" }
pallet-transaction-payment = { default-features = false, version = "25.0.0" }
pallet-transaction-payment-rpc-runtime-api = { default-features = false, version = "25.0.0" }
pallet-treasury = { default-features = false , version = "24.0.0" }
pallet-utility = { default-features = false, version = "25.0.0" }
pallet-referenda = { default-features = false, version = "25.0.0" }
pallet-ranked-collective = { default-features = false, version = "25.0.0" }
Expand Down Expand Up @@ -92,6 +94,7 @@ runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-asset-rate/runtime-benchmarks",
"pallet-alliance/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-collator-selection/runtime-benchmarks",
Expand All @@ -105,6 +108,7 @@ runtime-benchmarks = [
"pallet-salary/runtime-benchmarks",
"pallet-scheduler/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"pallet-treasury/runtime-benchmarks",
"pallet-utility/runtime-benchmarks",
"pallet-xcm/runtime-benchmarks",
"polkadot-parachain-primitives/runtime-benchmarks",
Expand All @@ -123,6 +127,7 @@ try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"frame-try-runtime/try-runtime",
"pallet-asset-rate/try-runtime",
"pallet-alliance/try-runtime",
"pallet-aura/try-runtime",
"pallet-authorship/try-runtime",
Expand All @@ -140,6 +145,7 @@ try-runtime = [
"pallet-session/try-runtime",
"pallet-timestamp/try-runtime",
"pallet-transaction-payment/try-runtime",
"pallet-treasury/try-runtime",
"pallet-utility/try-runtime",
"pallet-xcm/try-runtime",
"parachain-info/try-runtime",
Expand All @@ -164,6 +170,7 @@ std = [
"frame-system/std",
"frame-try-runtime?/std",
"log/std",
"pallet-asset-rate/std",
"pallet-alliance/std",
"pallet-aura/std",
"pallet-authorship/std",
Expand All @@ -182,6 +189,7 @@ std = [
"pallet-timestamp/std",
"pallet-transaction-payment-rpc-runtime-api/std",
"pallet-transaction-payment/std",
"pallet-treasury/std",
"pallet-utility/std",
"pallet-xcm/std",
"parachain-info/std",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,35 @@
mod origins;
mod tracks;
use crate::{
impls::ToParentTreasury, weights, AccountId, Balance, Balances, FellowshipReferenda,
GovernanceLocation, PolkadotTreasuryAccount, Preimage, Runtime, RuntimeCall, RuntimeEvent,
RuntimeOrigin, Scheduler, DAYS,
impls::ToParentTreasury, weights, xcm_config::TreasurerBodyId, AccountId, AssetRate, Balance,
Balances, FellowshipReferenda, GovernanceLocation, PolkadotTreasuryAccount, Preimage, Runtime,
RuntimeCall, RuntimeEvent, RuntimeOrigin, Scheduler, DAYS,
};
use cumulus_primitives_core::Junction::GeneralIndex;
use frame_support::{
parameter_types,
traits::{EitherOf, EitherOfDiverse, MapSuccess, OriginTrait, TryWithMorphedArg},
traits::{
EitherOf, EitherOfDiverse, MapSuccess, OriginTrait, PalletInfoAccess, TryWithMorphedArg,
},
PalletId,
};
use frame_system::EnsureRootWithSuccess;
use frame_system::{EnsureRoot, EnsureRootWithSuccess};
pub use origins::{
pallet_origins as pallet_fellowship_origins, Architects, EnsureCanPromoteTo, EnsureCanRetainAt,
EnsureFellowship, Fellows, Masters, Members, ToVoice,
};
use pallet_ranked_collective::EnsureOfRank;
use pallet_xcm::{EnsureXcm, IsVoiceOfBody};
use parachains_common::polkadot::account;
use polkadot_runtime_constants::{time::HOURS, xcm::body::FELLOWSHIP_ADMIN_INDEX};
use polkadot_runtime_common::impls::{
LocatableAssetConverter, VersionedLocatableAsset, VersionedMultiLocationConverter,
};
use polkadot_runtime_constants::{currency::GRAND, time::HOURS, xcm::body::FELLOWSHIP_ADMIN_INDEX};
use sp_arithmetic::Permill;
use sp_core::{ConstU128, ConstU32};
use sp_runtime::traits::{AccountIdConversion, ConstU16, ConvertToValue, Replace, TakeFirst};
use sp_runtime::traits::{
AccountIdConversion, ConstU16, ConvertToValue, IdentityLookup, Replace, TakeFirst,
};
use xcm::latest::BodyId;
use xcm_builder::{AliasesIntoAccountId32, LocatableAssetId, PayOverXcm};

Expand Down Expand Up @@ -244,3 +253,124 @@ impl pallet_salary::Config<FellowshipSalaryInstance> for Runtime {
// Total monthly salary budget.
type Budget = ConstU128<{ 100_000 * USDT_UNITS }>;
}

parameter_types! {
// TODO: reference the constant value from common crate when polkadot-sdk 1.5 is released.
// https://github.com/polkadot-fellows/runtimes/issues/113
pub const FellowshipTreasuryPalletId: PalletId = PalletId(*b"py/feltr");
pub const ProposalBond: Permill = Permill::from_percent(100);
pub const Burn: Permill = Permill::from_percent(0);
pub const MaxBalance: Balance = Balance::max_value();
// The asset's interior location for the paying account. This is the Fellowship Treasury
// pallet instance (which sits at index 65).
pub FellowshipTreasuryInteriorLocation: InteriorMultiLocation =
PalletInstance(<crate::FellowshipTreasury as PalletInfoAccess>::index() as u8).into();
}

/// [`PayOverXcm`] setup to pay the Fellowship Treasury.
pub type FellowshipTreasuryPaymaster = PayOverXcm<
FellowshipTreasuryInteriorLocation,
crate::xcm_config::XcmRouter,
crate::PolkadotXcm,
ConstU32<{ 6 * HOURS }>,
VersionedMultiLocation,
VersionedLocatableAsset,
LocatableAssetConverter,
VersionedMultiLocationConverter,
>;

pub type FellowshipTreasuryInstance = pallet_treasury::Instance1;

impl pallet_treasury::Config<FellowshipTreasuryInstance> for Runtime {
// The creation of proposals via the treasury pallet is deprecated and should not be utilized.
// Instead, public or fellowship referenda should be used to propose and command the treasury
// spend or spend_local dispatchables. The parameters below have been configured accordingly to
// discourage its use.
// TODO: replace with `NeverEnsure` once polkadot-sdk 1.5 is released.
// https://github.com/polkadot-fellows/runtimes/issues/113
type ApproveOrigin = EnsureRoot<AccountId>;
type OnSlash = ();
type ProposalBond = ProposalBond;
type ProposalBondMinimum = MaxBalance;
type ProposalBondMaximum = MaxBalance;
// end.

type WeightInfo = weights::pallet_treasury::WeightInfo<Runtime>;
type RuntimeEvent = RuntimeEvent;
type PalletId = FellowshipTreasuryPalletId;
type Currency = Balances;
type RejectOrigin = EitherOfDiverse<
EnsureRoot<AccountId>,
EitherOfDiverse<EnsureXcm<IsVoiceOfBody<GovernanceLocation, TreasurerBodyId>>, Fellows>,
>;
type SpendPeriod = ConstU32<{ 7 * DAYS }>;
type Burn = Burn;
type BurnDestination = ();
type SpendFunds = ();
type MaxApprovals = ConstU32<100>;
type SpendOrigin = EitherOf<
EitherOf<
EnsureRootWithSuccess<AccountId, MaxBalance>,
MapSuccess<
EnsureXcm<IsVoiceOfBody<GovernanceLocation, TreasurerBodyId>>,
Replace<ConstU128<{ 10_000 * GRAND }>>,
>,
>,
EitherOf<
MapSuccess<Architects, Replace<ConstU128<{ 10_000 * GRAND }>>>,
MapSuccess<Fellows, Replace<ConstU128<{ 10 * GRAND }>>>,
>,
>;
type AssetKind = VersionedLocatableAsset;
type Beneficiary = VersionedMultiLocation;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
#[cfg(not(feature = "runtime-benchmarks"))]
type Paymaster = FellowshipTreasuryPaymaster;
#[cfg(feature = "runtime-benchmarks")]
type Paymaster = PayWithEnsure<FellowshipTreasuryPaymaster, OpenHrmpChannel<ConstU32<1000>>>;
type BalanceConverter = AssetRate;
type PayoutPeriod = ConstU32<{ 30 * DAYS }>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = benchmarks::TreasuryArguments<sp_core::ConstU8<1>, ConstU32<1000>>;
}

// TODO: replace by [`polkadot_runtime_common::impls::benchmarks::TreasuryArguments`] when
// polkadot-sdk 1.5 is released.
// https://github.com/polkadot-fellows/runtimes/issues/113
#[cfg(feature = "runtime-benchmarks")]
mod benchmarks {
use super::VersionedLocatableAsset;
use core::marker::PhantomData;
use frame_support::traits::Get;
use pallet_treasury::ArgumentsFactory as TreasuryArgumentsFactory;
use sp_core::{ConstU32, ConstU8};
use xcm::prelude::*;

/// Provide factory methods for the [`VersionedLocatableAsset`] and the `Beneficiary` of the
/// [`VersionedMultiLocation`]. The location of the asset is determined as a Parachain with an
/// ID equal to the passed seed.
pub struct TreasuryArguments<Parents = ConstU8<0>, ParaId = ConstU32<0>>(
PhantomData<(Parents, ParaId)>,
);
impl<Parents: Get<u8>, ParaId: Get<u32>>
TreasuryArgumentsFactory<VersionedLocatableAsset, VersionedMultiLocation>
for TreasuryArguments<Parents, ParaId>
{
fn create_asset_kind(seed: u32) -> VersionedLocatableAsset {
VersionedLocatableAsset::V3 {
location: xcm::v3::MultiLocation::new(Parents::get(), X1(Parachain(ParaId::get()))),
asset_id: xcm::v3::MultiLocation::new(
0,
X2(PalletInstance(seed.try_into().unwrap()), GeneralIndex(seed.into())),
)
.into(),
}
}
fn create_beneficiary(seed: [u8; 32]) -> VersionedMultiLocation {
VersionedMultiLocation::V3(xcm::v3::MultiLocation::new(
0,
X1(AccountId32 { network: None, id: seed }),
))
}
}
}
Loading

0 comments on commit 49a04e5

Please sign in to comment.