diff --git a/Cargo.lock b/Cargo.lock index 4e2272bdc988..f040768a8e6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14426,6 +14426,33 @@ dependencies = [ "sp-staking 36.0.0", ] +[[package]] +name = "pallet-opf" +version = "0.1.0" +dependencies = [ + "frame-benchmarking 28.0.0", + "frame-support 28.0.0", + "frame-system 28.0.0", + "log", + "pallet-assets 29.1.0", + "pallet-balances 28.0.0", + "pallet-conviction-voting 28.0.0", + "pallet-democracy 28.0.0", + "pallet-preimage 28.0.0", + "pallet-scheduler 29.0.0", + "pallet-sudo 28.0.0", + "pallet-timestamp 27.0.0", + "pallet-transaction-payment 28.0.0", + "pallet-transaction-payment-rpc-runtime-api 28.0.0", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-runtime 31.0.1", + "sp-std 14.0.0", +] + [[package]] name = "pallet-paged-list" version = "0.6.0" @@ -18637,6 +18664,7 @@ dependencies = [ "pallet-nomination-pools-runtime-api 23.0.0", "pallet-offences 27.0.0", "pallet-offences-benchmarking 28.0.0", + "pallet-opf", "pallet-paged-list 0.6.0", "pallet-parameters 0.1.0", "pallet-preimage 28.0.0", diff --git a/Cargo.toml b/Cargo.toml index c30a9949e85e..3ae7c56ac16e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -391,6 +391,7 @@ members = [ "substrate/frame/nomination-pools/test-transfer-stake", "substrate/frame/offences", "substrate/frame/offences/benchmarking", + "substrate/frame/opf", "substrate/frame/paged-list", "substrate/frame/paged-list/fuzzer", "substrate/frame/parameters", @@ -965,6 +966,7 @@ pallet-nomination-pools-benchmarking = { path = "substrate/frame/nomination-pool pallet-nomination-pools-runtime-api = { path = "substrate/frame/nomination-pools/runtime-api", default-features = false } pallet-offences = { path = "substrate/frame/offences", default-features = false } pallet-offences-benchmarking = { path = "substrate/frame/offences/benchmarking", default-features = false } +pallet-opf = { path = "substrate/frame/opf", default-features = false } pallet-paged-list = { path = "substrate/frame/paged-list", default-features = false } pallet-parachain-template = { path = "templates/parachain/pallets/template", default-features = false } pallet-parameters = { path = "substrate/frame/parameters", default-features = false } diff --git a/prdoc/pr_6994.prdoc b/prdoc/pr_6994.prdoc new file mode 100644 index 000000000000..32805f2c145e --- /dev/null +++ b/prdoc/pr_6994.prdoc @@ -0,0 +1,21 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Optimistic Project Funding" + +doc: + - audience: Runtime Dev + description: | + the PF pallet handles the Optimistic Project Funding. + It allows users to nominate projects (whitelisted in OpenGov) with their DOT. + This mechanism will be funded with a constant stream of DOT taken directly from inflation and distributed to projects based on the proportion of DOT that has nominated them. + +crates: + - name: pallet-opf + bump: patch + - name: frame-support + bump: none + - name: polkadot-sdk + bump: patch + - name: polkadot + bump: none \ No newline at end of file diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 93b134e8165f..80fc5d88047a 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1589,6 +1589,54 @@ impl pallet_offences::Config for Runtime { type OnOffenceHandler = Staking; } + +parameter_types! { + // Id of the treasury + pub const PotId: PalletId = PalletId(*b"py/potid"); + pub const ClaimingPeriod: BlockNumber = 7 * DAYS; + pub const VoteValidityPeriod: BlockNumber = 7 * DAYS; + pub const MaxProjects:u32 = 50; + + /// This should be calculated as a percentage of inflation. + pub const TemporaryRewards: Balance = 100000 * DOLLARS; + + +} +impl pallet_opf::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type NativeBalance = Balances; + type AdminOrigin = EnsureRoot; + /// Pot PalletId + type PotId = PotId; + + /// A reason for placing a hold on funds. + type RuntimeHoldReason = RuntimeHoldReason; + + /// Maximum number of whitelisted projects + type MaxProjects = MaxProjects; + + /// Time period in which people can vote. + /// After the period has ended, the votes are counted (STOP THE COUNT) + /// and then the funds are distributed into Spends. + type VotingPeriod = VotingPeriod; + + /// Time for claiming a Spend. + /// After the period has passed, a spend is thrown away + /// and the funds are available again for distribution in the pot. + type ClaimingPeriod = ClaimingPeriod; + + /// Period after which all the votes are resetted. + type VoteValidityPeriod = VoteValidityPeriod; + + type BlockNumberProvider = System; + + /// This should be calculated as a percentage of inflation. + type TemporaryRewards = TemporaryRewards; + + type WeightInfo = (); //pallet_opf::weights::SubstrateWeight; +} + impl pallet_authority_discovery::Config for Runtime { type MaxAuthorities = MaxAuthorities; } @@ -2635,6 +2683,9 @@ mod runtime { #[runtime::pallet_index(81)] pub type VerifySignature = pallet_verify_signature::Pallet; + + #[runtime::pallet_index(82)] + pub type Opf = pallet_opf::Pallet; } impl TryFrom for pallet_revive::Call { @@ -2897,6 +2948,7 @@ mod benches { [pallet_example_mbm, PalletExampleMbms] [pallet_asset_conversion_ops, AssetConversionMigration] [pallet_verify_signature, VerifySignature] + [pallet_opf, Opf] ); } diff --git a/substrate/frame/opf/Cargo.toml b/substrate/frame/opf/Cargo.toml new file mode 100644 index 000000000000..d2c7c662d39b --- /dev/null +++ b/substrate/frame/opf/Cargo.toml @@ -0,0 +1,99 @@ +[package] +authors.workspace = true +description = "Optimist Project Funding - pallet allowing users to nominate projects to be funded, by locking their DOTS." +edition.workspace = true +homepage = "https://substrate.io" +license = "Apache-2.0" +name = "pallet-opf" +readme = "README.md" +repository.workspace = true +version = "0.1.0" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true, default-features = false } +frame-benchmarking = { optional = true, workspace = true, default-features = false } +frame-support = { workspace = true, default-features = false } +frame-system = { workspace = true, default-features = false } +log = { workspace = true } +pallet-conviction-voting = { workspace = true, default-features = false } +pallet-democracy = { workspace = true, default-features = true } +pallet-scheduler = { workspace = true, default-features = false } +scale-info = { features = [ + "derive", +], workspace = true, default-features = false } +serde = { optional = true, workspace = true, default-features = true } +sp-core = { workspace = true, default-features = false } +sp-io = { workspace = true, default-features = false } +sp-runtime = { workspace = true, default-features = false } +sp-std = { workspace = true, default-features = true } + +[dev-dependencies] +pallet-assets = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } +pallet-preimage = { workspace = true, default-features = true } +pallet-scheduler = { workspace = true, default-features = true } +pallet-sudo = { workspace = true, default-features = true } +pallet-timestamp = { workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true, default-features = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true } + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-democracy/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", + "scale-info/std", + "serde", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-assets/std", + "pallet-balances/std", + "pallet-conviction-voting/std", + "pallet-democracy/std", + "pallet-preimage/std", + "pallet-scheduler/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-democracy/try-runtime", + "pallet-preimage/try-runtime", + "pallet-scheduler/try-runtime", + "pallet-sudo/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs new file mode 100644 index 000000000000..863cddad927f --- /dev/null +++ b/substrate/frame/opf/src/functions.rs @@ -0,0 +1,166 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! Helper functions for OPF pallet. + +pub use super::*; +impl Pallet { + pub fn pot_account() -> AccountIdOf { + // Get Pot account + T::PotId::get().into_account_truncating() + } + + /// Funds transfer from the Pot to a project account + pub fn spend(amount: BalanceOf, beneficiary: AccountIdOf) -> DispatchResult { + // Get Pot account + let pot_account: AccountIdOf = Self::pot_account(); + + //Operate the transfer + T::NativeBalance::transfer(&pot_account, &beneficiary, amount, Preservation::Preserve)?; + + Ok(()) + } + + /// Series of checks on the Pot, to ensure that we have enough funds + /// before executing a Spend --> used in tests. + pub fn pot_check(spend: BalanceOf) -> DispatchResult { + // Get Pot account + let pot_account = Self::pot_account(); + + // Check that the Pot as enough funds for the transfer + let balance = T::NativeBalance::balance(&pot_account); + let minimum_balance = T::NativeBalance::minimum_balance(); + let remaining_balance = balance.saturating_sub(spend); + + ensure!(remaining_balance > minimum_balance, Error::::InsufficientPotReserves); + ensure!(balance > spend, Error::::InsufficientPotReserves); + Ok(()) + } + + // Voting Period checks + pub fn period_check() -> DispatchResult { + // Get current voting round & check if we are in voting period or not + let current_round_index = NextVotingRoundNumber::::get().saturating_sub(1); + let round = VotingRounds::::get(current_round_index).ok_or(Error::::NoRoundFound)?; + let now = T::BlockNumberProvider::current_block_number(); + ensure!(now < round.round_ending_block, Error::::VotingRoundOver); + Ok(()) + } + + pub fn unlist_project(project_id: ProjectId) -> DispatchResult { + WhiteListedProjectAccounts::::remove(&project_id); + + Ok(()) + } + + // The total reward to be distributed is a portion or inflation, determined in another pallet + // Reward calculation is executed within the Voting period + pub fn calculate_rewards(total_reward: BalanceOf) -> DispatchResult { + let projects: Vec> = WhiteListedProjectAccounts::::iter_keys().collect(); + if projects.is_empty() { return Ok(()) } + let round_number = NextVotingRoundNumber::::get().saturating_sub(1); + let round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; + if projects.clone().len() > 0 as usize { + let total_positive_votes_amount = round.total_positive_votes_amount; + let total_negative_votes_amount = round.total_negative_votes_amount; + let when = T::BlockNumberProvider::current_block_number(); + let total_votes_amount = + total_positive_votes_amount.saturating_sub(total_negative_votes_amount); + + // for each project, calculate the percentage of votes, the amount to be distributed, + // and then populate the storage Projects + for project_id in projects { + if ProjectFunds::::contains_key(&project_id) { + let funds = ProjectFunds::::get(&project_id); + let project_positive_reward = funds[0]; + let project_negative_reward = funds[1]; + + if project_positive_reward > project_negative_reward { + let project_reward = + project_positive_reward.saturating_sub(project_negative_reward); + + let project_percentage = + Percent::from_rational(project_reward, total_votes_amount); + let final_amount = project_percentage * total_reward; + + // Send calculated reward for reward distribution + let project_info = ProjectInfo { + project_id: project_id.clone(), + submission_block: when, + amount: final_amount, + }; + + // create a spend for project to be rewarded + let _ = SpendInfo::::new(&project_info); + + Self::deposit_event(Event::::ProjectFundingAccepted { + project_id: project_id.clone(), + when, + round_number, + amount: project_info.amount, + }) + } else { + Self::deposit_event(Event::::ProjectFundingRejected { + when, + project_id: project_id.clone(), + }) + } + } + } + } + + Ok(()) + } + + // To be executed in a hook, on_initialize + pub fn on_idle_function(limit: Weight) -> Weight { + let now = T::BlockNumberProvider::current_block_number(); + let mut meter = WeightMeter::with_limit(limit); + let max_block_weight = T::DbWeight::get().reads_writes(14, 8); + + if meter.try_consume(max_block_weight).is_err() { + return meter.consumed(); + } + let mut round_index = NextVotingRoundNumber::::get(); + + // No active round? + if round_index == 0 { + // Start the first voting round + let _round0 = VotingRoundInfo::::new(); + round_index = NextVotingRoundNumber::::get(); + } + + let current_round_index = round_index.saturating_sub(1); + + let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); + let round_ending_block = round_infos.round_ending_block; + + // Conditions for reward distribution preparations are: + // - We are at the end of voting_round period + if now > round_ending_block { + + // Clear ProjectFunds storage + ProjectFunds::::drain(); + // Emmit events + Self::deposit_event(Event::::VotingRoundEnded { + when: now, + round_number: round_infos.round_number, + }); + } + + meter.consumed() + } +} diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs new file mode 100644 index 000000000000..9858033cb899 --- /dev/null +++ b/substrate/frame/opf/src/lib.rs @@ -0,0 +1,403 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; +mod functions; +mod types; +pub use pallet_democracy as Democracy; +pub use types::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + use frame_system::WeightInfo; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + Democracy::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type RuntimeCall: Convert<::RuntimeCall, ::RuntimeCall> + + Parameter + + UnfilteredDispatchable::RuntimeOrigin> + + From> + + GetDispatchInfo; + /// The admin origin that can list and un-list whitelisted projects. + type AdminOrigin: EnsureOrigin; + + /// Type to access the Balances Pallet. + type NativeBalance: fungible::Inspect + + fungible::Mutate + + fungible::hold::Inspect + + fungible::hold::Mutate + + fungible::freeze::Inspect + + fungible::freeze::Mutate; + + type RuntimeHoldReason: From; + /// Provider for the block number. + type BlockNumberProvider: BlockNumberProvider; + + /// Treasury account Id + #[pallet::constant] + type PotId: Get; + + /// Time period in which people can vote. + /// After the period has ended, the votes are counted (STOP THE COUNT) + /// and then the funds are distributed into Spends. + #[pallet::constant] + type VotingPeriod: Get>; + + /// Maximum number projects that can be accepted by this pallet + #[pallet::constant] + type MaxProjects: Get; + + /// Time for claiming a Spend. + /// After the period has passed, a spend is thrown away + /// and the funds are available again for distribution in the pot. + #[pallet::constant] + type ClaimingPeriod: Get>; + + /// Period after which all the votes are reset. + #[pallet::constant] + type VoteValidityPeriod: Get>; + + /// Used for Pallet testing only. Represents the Total Reward distributed + type TemporaryRewards: Get>; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + /// A reason for placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// Funds are held for a given buffer time before payment + #[codec(index = 0)] + FundsReserved, + } + + /// Number of Voting Rounds executed so far + #[pallet::storage] + pub type NextVotingRoundNumber = StorageValue<_, u32, ValueQuery>; + + /// Returns Infos about a Voting Round agains the Voting Round index + #[pallet::storage] + pub type VotingRounds = + StorageMap<_, Twox64Concat, RoundIndex, VotingRoundInfo, OptionQuery>; + + /// Spends that still have to be claimed. + #[pallet::storage] + pub(super) type Spends = + CountedStorageMap<_, Twox64Concat, ProjectId, SpendInfo, OptionQuery>; + + /// List of Whitelisted Project registered + #[pallet::storage] + pub type WhiteListedProjectAccounts = + CountedStorageMap<_, Twox64Concat, ProjectId, ProjectInfo, OptionQuery>; + + /// Returns (positive_funds,negative_funds) of Whitelisted Project accounts + #[pallet::storage] + pub type ProjectFunds = StorageMap< + _, + Twox64Concat, + ProjectId, + BoundedVec, ConstU32<2>>, + ValueQuery, + >; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Reward successfully claimed + RewardClaimed { + when: ProvidedBlockNumberFor, + amount: BalanceOf, + project_id: ProjectId, + }, + + /// A Spend was created + SpendCreated { + when: ProvidedBlockNumberFor, + amount: BalanceOf, + project_id: ProjectId, + }, + + /// Not yet in the claiming period + NotClaimingPeriod { project_id: ProjectId, claiming_period: ProvidedBlockNumberFor }, + + /// Payment will be enacted for corresponding project + WillBeEnacted { project_id: ProjectId }, + + /// Reward successfully assigned + RewardsAssigned { when: ProvidedBlockNumberFor }, + + /// User's vote successfully submitted + VoteCasted { who: VoterId, when: ProvidedBlockNumberFor, project_id: ProjectId }, + + /// User's vote successfully removed + VoteRemoved { who: VoterId, when: ProvidedBlockNumberFor, project_id: ProjectId }, + + /// Project added to whitelisted projects list + Projectlisted { when: ProvidedBlockNumberFor, project_id: ProjectId }, + + /// Several projects added to whitelisted projects list + Projectslisted { when: ProvidedBlockNumberFor, projects_id: Vec> }, + + /// Project removed from whitelisted projects list + ProjectUnlisted { when: ProvidedBlockNumberFor, project_id: ProjectId }, + + /// Project Funding Accepted by voters + ProjectFundingAccepted { + project_id: ProjectId, + when: ProvidedBlockNumberFor, + round_number: u32, + amount: BalanceOf, + }, + + /// Reward claim has expired + ExpiredClaim { expired_when: ProvidedBlockNumberFor, project_id: ProjectId }, + + /// Project Funding rejected by voters + ProjectFundingRejected { when: ProvidedBlockNumberFor, project_id: ProjectId }, + + /// A new voting round started + VotingRoundStarted { when: ProvidedBlockNumberFor, round_number: u32 }, + + /// The users voting period ended. Reward calculation will start. + VoteActionLocked { when: ProvidedBlockNumberFor, round_number: u32 }, + + /// The voting round ended + VotingRoundEnded { when: ProvidedBlockNumberFor, round_number: u32 }, + } + + #[pallet::error] + pub enum Error { + /// Not enough Funds in the Pot + InsufficientPotReserves, + /// The funds transfer operation failed + TransferFailed, + /// Spend or Spend index does not exists + InexistentSpend, + /// No valid Account_id found + NoValidAccount, + /// No project available for funding + NoProjectAvailable, + /// The Funds transfer failed + FailedSpendOperation, + /// Still not in claiming period + NotClaimingPeriod, + /// Funds locking failed + FundsReserveFailed, + /// An invalid result was returned + InvalidResult, + /// The reward calculation failed due to an internal error + FailedRewardCalculation, + /// Voting round is over + VotingRoundOver, + /// This voting round does not exists + NoRoundFound, + /// Maximum number of projects submission for reward distribution as been reached + MaximumProjectsNumber, + /// Another project has already been submitted under the same project_id + SubmittedProjectId, + /// Project batch already submitted + BatchAlreadySubmitted + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_idle(_n: SystemBlockNumberFor, remaining_weight: Weight) -> Weight { + Self::on_idle_function(remaining_weight) + } + } + + #[pallet::call] + impl Pallet { + + /// OPF Projects registration + /// + /// ## Dispatch Origin + /// + /// Must be AdminOrigin + /// + /// ## Details + /// + /// From this extrinsic only AdminOrigin can register project. + /// + /// ### Parameters + /// - `projects_id`: The accounts that might be funded. + /// + /// ### Errors + /// - [`Error::::MaximumProjectsNumber`]: Maximum number of project subscriptions reached + /// + /// ## Events + /// Emits [`Event::::Projectslisted`]. + #[pallet::call_index(1)] + pub fn register_projects_batch(origin: OriginFor, projects_id: Vec>) -> DispatchResult { + //T::AdminOrigin::ensure_origin_or_root(origin.clone())?; + let who = T::SubmitOrigin::ensure_origin(origin.clone())?; + // Only 1 batch submission per round + let mut round_index = NextVotingRoundNumber::::get(); + + // No active round? + if round_index == 0 { + // Start the first voting round + let _round0 = VotingRoundInfo::::new(); + round_index = NextVotingRoundNumber::::get(); + } + + let current_round_index = round_index.saturating_sub(1); + + let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); + // Check no Project batch has been submitted yet + ensure!(round_infos.batch_submitted == false, Error::::BatchAlreadySubmitted); + let round_ending_block = round_infos.round_ending_block; + + // If current voting round is over, start a new one + let when = T::BlockNumberProvider::current_block_number(); + if when >= round_ending_block { + // Create a new round. + let _new_round = VotingRoundInfo::::new(); + } + + for project_id in &projects_id{ + ProjectInfo::::new(project_id.clone()); + // Prepare the proposal call + let call0: ::RuntimeCall = crate::Call::::on_registration {project_id: project_id.clone()}.into(); + let call = ::RuntimeCall::convert(call0); + let call_f = T::Preimages::bound(call).unwrap(); + let threshold = Democracy::VoteThreshold::SimpleMajority; + Democracy::Pallet::::propose(origin.clone(), call_f.clone(), T::MinimumDeposit::get())?; + Democracy::Pallet::::internal_start_referendum(call_f,threshold, Zero::zero()); + + } + + Self::deposit_event(Event::Projectslisted { when, projects_id }); + Ok(()) + } + + /// OPF Projects de-listing + /// + /// ## Dispatch Origin + /// + /// Must be signed + /// + /// ## Details + /// + /// From this extrinsic only Root can de-list a project. + /// + /// ### Parameters + /// - `project_id`: The account that will receive the reward. + /// + /// ### Errors + /// - [`Error::::NoProjectAvailable`]: No project found under this project_id + /// + /// ## Events + /// Emits [`Event::::ProjectUnlisted`]. + #[pallet::call_index(2)] + #[transactional] + pub fn unregister_project( + origin: OriginFor, + project_id: ProjectId, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin_or_root(origin)?; + let when = T::BlockNumberProvider::current_block_number(); + Self::unlist_project(project_id.clone())?; + Self::deposit_event(Event::::ProjectUnlisted { when, project_id }); + + Ok(()) + } + + #[pallet::call_index(3)] + #[transactional] + pub fn vote(origin: OriginFor) -> DispatchResult { + Ok(()) + } + + #[pallet::call_index(4)] + #[transactional] + pub fn remove_vote(origin: OriginFor) -> DispatchResult { + Ok(()) + } + + /// OPF Reward Claim logic + /// + /// ## Dispatch Origin + /// + /// Must be signed + /// + /// ## Details + /// + /// From this extrinsic any user can claim a reward for a nominated/whitelisted project. + /// + /// ### Parameters + /// - `project_id`: The account that will receive the reward. + /// + /// ### Errors + /// - [`Error::::InexistentSpend`]:Spend or Spend index does not exists + /// - [`Error::::NoValidAccount`]: No valid Account_id found + /// - [`Not Claiming Period`]: Still not in claiming period + /// + /// ## Events + /// Emits [`Event::::RewardClaimed`] if successful for a positive approval. + #[pallet::call_index(5)] + #[transactional] + pub fn claim_reward_for(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let _caller = ensure_signed(origin)?; + let now = T::BlockNumberProvider::current_block_number(); + let info = Spends::::get(&project_id).ok_or(Error::::InexistentSpend)?; + if now >= info.expire { + Spends::::remove(&project_id); + Self::deposit_event(Event::ExpiredClaim { + expired_when: info.expire, + project_id, + }); + Ok(()) + } else if now < info.expire { + // transfer the funds + Self::spend(info.amount, project_id.clone())?; + Self::deposit_event(Event::RewardClaimed { + when: now, + amount: info.amount, + project_id: project_id.clone(), + }); + Self::unlist_project(project_id)?; + Ok(()) + } else { + Err(DispatchError::Other("Not Claiming Period")) + } + } + + #[pallet::call_index(6)] + #[transactional] + pub fn on_registration(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + // prepare reward distribution + // for now we are using the temporary-constant reward. + let _ = Self::calculate_rewards(T::TemporaryRewards::get())?; + Ok(()) + } + } +} diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs new file mode 100644 index 000000000000..e6bbb19cb443 --- /dev/null +++ b/substrate/frame/opf/src/mock.rs @@ -0,0 +1,210 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Test environment for OPF pallet. +use crate as pallet_opf; +use codec::{Decode, Encode, MaxEncodedLen}; +use crate::Convert; +pub use frame_support::{ + derive_impl, ord_parameter_types, + pallet_prelude::TypeInfo, + parameter_types, + traits::{ + ConstU32, ConstU64, SortedMembers, EqualPrivilegeOnly, OnFinalize, OnInitialize, OriginTrait, VoteTally, + }, + weights::Weight, + PalletId, +}; +pub use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy}; +pub use sp_runtime::{ + traits::{AccountIdConversion, IdentityLookup}, + BuildStorage, Perbill, +}; +pub type Block = frame_system::mocking::MockBlock; +pub type Balance = u64; +pub type AccountId = u64; + + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub struct Test { + System: frame_system, + Balances: pallet_balances, + Preimage: pallet_preimage, + Scheduler: pallet_scheduler, + Opf: pallet_opf, + Democracy: pallet_democracy, + } +); + +parameter_types! { + pub MaxWeight: Weight = Weight::from_parts(2_000_000_000_000, u64::MAX); +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type AccountId = AccountId; + type AccountData = pallet_balances::AccountData; + type Block = Block; + type Lookup = IdentityLookup; +} + +impl pallet_preimage::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = (); +} +impl pallet_scheduler::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaxWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<100>; + type WeightInfo = (); + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = Preimage; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type AccountStore = System; +} + +parameter_types! { + pub static PreimageByteDeposit: u64 = 0; + pub static InstantAllowed: bool = false; +} +ord_parameter_types! { + pub const One: u64 = 1; + pub const Two: u64 = 2; + pub const Three: u64 = 3; + pub const Four: u64 = 4; + pub const Five: u64 = 5; + pub const Six: u64 = 6; +} + +pub struct OneToFive; +impl SortedMembers for OneToFive { + fn sorted_members() -> Vec { + vec![1, 2, 3, 4, 5] + } + #[cfg(feature = "runtime-benchmarks")] + fn add(_m: &u64) {} +} + +impl pallet_democracy::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = pallet_balances::Pallet; + type EnactmentPeriod = ConstU64<2>; + type LaunchPeriod = ConstU64<2>; + type VotingPeriod = ConstU64<2>; + type VoteLockingPeriod = ConstU64<3>; + type FastTrackVotingPeriod = ConstU64<2>; + type MinimumDeposit = ConstU64<1>; + type MaxDeposits = ConstU32<1000>; + type MaxBlacklisted = ConstU32<5>; + type SubmitOrigin = EnsureSigned; + type ExternalOrigin = EnsureSignedBy; + type ExternalMajorityOrigin = EnsureSignedBy; + type ExternalDefaultOrigin = EnsureSignedBy; + type FastTrackOrigin = EnsureSignedBy; + type CancellationOrigin = EnsureSignedBy; + type BlacklistOrigin = EnsureRoot; + type CancelProposalOrigin = EnsureRoot; + type VetoOrigin = EnsureSignedBy; + type CooloffPeriod = ConstU64<2>; + type Slash = (); + type InstantOrigin = EnsureSignedBy; + type InstantAllowed = InstantAllowed; + type Scheduler = Scheduler; + type MaxVotes = ConstU32<100>; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); + type MaxProposals = ConstU32<100>; + type Preimages = Preimage; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"py/potid"); + pub const MaxProjects:u32 = 50; + pub const TemporaryRewards: Balance = 100_000; + pub const VoteLockingPeriod:u32 = 10; + pub const VotingPeriod:u32 = 30; +} +impl pallet_opf::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type AdminOrigin = frame_system::EnsureRoot; + type NativeBalance = Balances; + type PotId = PotId; + type RuntimeHoldReason = RuntimeHoldReason; + type MaxProjects = MaxProjects; + type VotingPeriod = VotingPeriod; + type ClaimingPeriod = VotingPeriod; + type VoteValidityPeriod = VotingPeriod; + type BlockNumberProvider = System; + type TemporaryRewards = TemporaryRewards; + type WeightInfo = (); +} + +impl Convert for RuntimeCall { + fn convert(call: RuntimeCall) -> RuntimeCall { + let call_encoded: Vec = call.encode(); + let ref_call_encoded = &call_encoded; + if let Ok(call_formatted) = RuntimeCall::decode(&mut &ref_call_encoded[..]){ + call_formatted + } else{ + call + } + } +} + +//Define some accounts and use them +pub const ALICE: AccountId = 10; +pub const BOB: AccountId = 11; +pub const DAVE: AccountId = 12; +pub const EVE: AccountId = 13; +pub const BSX: Balance = 100_000_000_000; + +pub fn expect_events(e: Vec) { + e.into_iter().for_each(frame_system::Pallet::::assert_has_event); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let pot_account = PotId::get().into_account_truncating(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, 200_000 * BSX), + (BOB, 200_000 * BSX), + (DAVE, 150_000 * BSX), + (EVE, 150_000 * BSX), + (pot_account, 150_000_000 * BSX), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs new file mode 100644 index 000000000000..18a3b29baad0 --- /dev/null +++ b/substrate/frame/opf/src/tests.rs @@ -0,0 +1,62 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for OPF pallet. + +pub use super::*; +use crate::mock::*; +use frame_support::{assert_noop, assert_ok, traits::OnIdle}; + +pub fn next_block() { + System::set_block_number(::BlockNumberProvider::current_block_number() + 1); + AllPalletsWithSystem::on_initialize( + ::BlockNumberProvider::current_block_number(), + ); + AllPalletsWithSystem::on_idle( + ::BlockNumberProvider::current_block_number(), + Weight::MAX, + ); +} + +pub fn project_list() -> Vec>{ + vec![ALICE, BOB, DAVE] + +} + +pub fn run_to_block(n: BlockNumberFor) { + while ::BlockNumberProvider::current_block_number() < n { + if ::BlockNumberProvider::current_block_number() > 1 { + AllPalletsWithSystem::on_finalize( + ::BlockNumberProvider::current_block_number(), + ); + } + next_block(); + } +} + +#[test] +fn project_registration_works() { + new_test_ext().execute_with(|| { + let batch = project_list(); + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); + let project_list = WhiteListedProjectAccounts::::get(BOB); + assert!(project_list.is_some()); + // we should have 3 referendum started + assert_eq!(pallet_democracy::PublicProps::::get().len(), 3); + assert_eq!(pallet_democracy::ReferendumCount::::get(), 3); + }) +} diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs new file mode 100644 index 000000000000..ae6ac1581654 --- /dev/null +++ b/substrate/frame/opf/src/types.rs @@ -0,0 +1,221 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types & Imports for Distribution pallet. + +pub use super::*; + +pub use frame_support::{ + pallet_prelude::*, + traits::UnfilteredDispatchable, + dispatch::GetDispatchInfo, + traits::{ + fungible, + fungible::{Inspect, Mutate, MutateHold}, + fungibles, + schedule::{ + v3::{Anon as ScheduleAnon, Named as ScheduleNamed}, + DispatchTime, MaybeHashed, + }, + tokens::{Precision, Preservation}, + Bounded, DefensiveOption, EnsureOrigin, LockIdentifier, OriginTrait, QueryPreimage, + StorePreimage, + }, + transactional, + weights::WeightMeter, + PalletId, Serialize, +}; +pub use frame_system::{pallet_prelude::*, RawOrigin}; +pub use pallet_conviction_voting::Conviction; +pub use scale_info::prelude::vec::Vec; +pub use sp_runtime::{ + traits::{ + AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, + Zero, + }, + Percent, +}; +pub use sp_std::boxed::Box; + +pub type BalanceOf = <::NativeBalance as fungible::Inspect< + ::AccountId, +>>::Balance; +pub type AccountIdOf = ::AccountId; +/// A reward index. +pub type SpendIndex = u32; +pub type CallOf = ::RuntimeCall; +pub type BoundedCallOf = Bounded, ::Hashing>; +pub type ProjectId = AccountIdOf; +pub type PalletsOriginOf = + <::RuntimeOrigin as OriginTrait>::PalletsOrigin; +pub const DISTRIBUTION_ID: LockIdentifier = *b"distribu"; +pub type RoundIndex = u32; +pub type VoterId = AccountIdOf; +pub type ProvidedBlockNumberFor = + <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; +pub use frame_system::pallet_prelude::BlockNumberFor as SystemBlockNumberFor; + +/// The state of the payment claim. +#[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo, Default)] +pub enum SpendState { + /// Unclaimed + #[default] + Unclaimed, + /// Claimed & Paid. + Completed, + /// Claimed but Failed. + Failed, +} + +//Processed Reward status +#[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct SpendInfo { + /// The asset amount of the spend. + pub amount: BalanceOf, + /// The block number from which the spend can be claimed(24h after SpendStatus Creation). + pub valid_from: ProvidedBlockNumberFor, + /// Corresponding project id + pub whitelisted_project: ProjectInfo, + /// Has it been claimed? + pub claimed: bool, + /// Claim Expiration block + pub expire: ProvidedBlockNumberFor, +} + +impl SpendInfo { + pub fn new(whitelisted: &ProjectInfo) -> Self { + let amount = whitelisted.amount; + let whitelisted_project = whitelisted.clone(); + let claimed = false; + let valid_from = T::BlockNumberProvider::current_block_number(); + let expire = valid_from.saturating_add(T::ClaimingPeriod::get()); + + let spend = SpendInfo { amount, valid_from, whitelisted_project, claimed, expire }; + + //Add it to the Spends storage + Spends::::insert(whitelisted.project_id.clone(), spend.clone()); + + spend + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct ProjectInfo { + /// AcountId that will receive the payment. + pub project_id: ProjectId, + + /// Block at which the project was submitted for reward distribution + pub submission_block: ProvidedBlockNumberFor, + + /// Amount to be locked & payed for this project + pub amount: BalanceOf, +} + +impl ProjectInfo { + pub fn new(project_id: ProjectId) { + let submission_block = T::BlockNumberProvider::current_block_number(); + let amount = Zero::zero(); + let project_info = ProjectInfo { project_id: project_id.clone(), submission_block, amount }; + WhiteListedProjectAccounts::::insert(project_id, project_info); + } + +} + +#[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct VoteInfo { + /// The amount of stake/slash placed on this vote. + pub amount: BalanceOf, + + /// Round at which the vote was casted + pub round: VotingRoundInfo, + + /// Whether the vote is "fund" / "not fund" + pub is_fund: bool, + + pub conviction: Conviction, + + pub funds_unlock_block: ProvidedBlockNumberFor, +} + +// If no conviction, user's funds are released at the end of the voting round +impl VoteInfo { + pub fn funds_unlock(&mut self) { + let conviction_coeff = >::from(self.conviction); + let funds_unlock_block = self + .round + .round_ending_block + .saturating_add(T::VoteValidityPeriod::get().saturating_mul(conviction_coeff.into())); + self.funds_unlock_block = funds_unlock_block; + } +} + +impl Default for VoteInfo { + // Dummy vote infos used to handle errors + fn default() -> Self { + // get round number + let round = VotingRounds::::get(0).expect("Round 0 exists"); + let amount = Zero::zero(); + let is_fund = false; + let conviction = Conviction::None; + let funds_unlock_block = round.round_ending_block; + VoteInfo { amount, round, is_fund, conviction, funds_unlock_block } + } +} + +/// Voting rounds are periodically created inside a hook on_initialize (use poll in the future) +#[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct VotingRoundInfo { + pub round_number: u32, + pub round_starting_block: ProvidedBlockNumberFor, + pub round_ending_block: ProvidedBlockNumberFor, + pub total_positive_votes_amount: BalanceOf, + pub total_negative_votes_amount: BalanceOf, + pub batch_submitted: bool, +} + +impl VotingRoundInfo { + pub fn new() -> Self { + let round_starting_block = T::BlockNumberProvider::current_block_number(); + let batch_submitted = false; + let round_ending_block = round_starting_block + .clone() + .saturating_add(::VotingPeriod::get()); + let round_number = NextVotingRoundNumber::::mutate(|n| { let res = *n; *n = n.saturating_add(1); res }); + let total_positive_votes_amount = BalanceOf::::zero(); + let total_negative_votes_amount = BalanceOf::::zero(); + + Pallet::::deposit_event(Event::::VotingRoundStarted { + when: round_starting_block, + round_number, + }); + + let round_infos = VotingRoundInfo { + round_number, + round_starting_block, + round_ending_block, + total_positive_votes_amount, + total_negative_votes_amount, + batch_submitted, + }; + VotingRounds::::insert(round_number, round_infos.clone()); + round_infos + } +} diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 17a7c02e8259..9a65a8eb2328 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -113,6 +113,7 @@ std = [ "pallet-nomination-pools?/std", "pallet-offences-benchmarking?/std", "pallet-offences?/std", + "pallet-opf?/std", "pallet-paged-list?/std", "pallet-parameters?/std", "pallet-preimage?/std", @@ -301,6 +302,7 @@ runtime-benchmarks = [ "pallet-nomination-pools?/runtime-benchmarks", "pallet-offences-benchmarking?/runtime-benchmarks", "pallet-offences?/runtime-benchmarks", + "pallet-opf?/runtime-benchmarks", "pallet-paged-list?/runtime-benchmarks", "pallet-parameters?/runtime-benchmarks", "pallet-preimage?/runtime-benchmarks", @@ -436,6 +438,7 @@ try-runtime = [ "pallet-node-authorization?/try-runtime", "pallet-nomination-pools?/try-runtime", "pallet-offences?/try-runtime", + "pallet-opf?/try-runtime", "pallet-paged-list?/try-runtime", "pallet-parameters?/try-runtime", "pallet-preimage?/try-runtime", @@ -497,6 +500,7 @@ serde = [ "pallet-democracy?/serde", "pallet-message-queue?/serde", "pallet-offences?/serde", + "pallet-opf?/serde", "pallet-parameters?/serde", "pallet-referenda?/serde", "pallet-remark?/serde", @@ -543,7 +547,7 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] -runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-weight-reclaim", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] +runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-weight-reclaim", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-opf", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] runtime = [ "frame-benchmarking", "frame-benchmarking-pallet-pov", @@ -1155,6 +1159,11 @@ default-features = false optional = true path = "../substrate/frame/offences/benchmarking" +[dependencies.pallet-opf] +default-features = false +optional = true +path = "../substrate/frame/opf" + [dependencies.pallet-paged-list] default-features = false optional = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 3504f081f295..9ad179df28a9 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -547,6 +547,11 @@ pub use pallet_offences; #[cfg(feature = "pallet-offences-benchmarking")] pub use pallet_offences_benchmarking; +/// Optimist Project Funding - pallet allowing users to nominate projects to be funded, by +/// locking their DOTS. +#[cfg(feature = "pallet-opf")] +pub use pallet_opf; + /// FRAME pallet that provides a paged list data structure. #[cfg(feature = "pallet-paged-list")] pub use pallet_paged_list;