Skip to main content

pezpallet_democracy/
lib.rs

1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Democracy Pezpallet
19//!
20//! - [`Config`]
21//! - [`Call`]
22//!
23//! ## Overview
24//!
25//! The Democracy pezpallet handles the administration of general stakeholder voting.
26//!
27//! There are two different queues that a proposal can be added to before it
28//! becomes a referendum, 1) the proposal queue consisting of all public proposals
29//! and 2) the external queue consisting of a single proposal that originates
30//! from one of the _external_ origins (such as a collective group).
31//!
32//! Every launch period - a length defined in the runtime - the Democracy pezpallet
33//! launches a referendum from a proposal that it takes from either the proposal
34//! queue or the external queue in turn. Any token holder in the system can vote
35//! on referenda. The voting system
36//! uses time-lock voting by allowing the token holder to set their _conviction_
37//! behind a vote. The conviction will dictate the length of time the tokens
38//! will be locked, as well as the multiplier that scales the vote power.
39//!
40//! ### Terminology
41//!
42//! - **Enactment Period:** The minimum period of locking and the period between a proposal being
43//! approved and enacted.
44//! - **Lock Period:** A period of time after proposal enactment that the tokens of _winning_ voters
45//! will be locked.
46//! - **Conviction:** An indication of a voter's strength of belief in their vote. An increase
47//! of one in conviction indicates that a token holder is willing to lock their tokens for twice
48//! as many lock periods after enactment.
49//! - **Vote:** A value that can either be in approval ("Aye") or rejection ("Nay") of a particular
50//!   referendum.
51//! - **Proposal:** A submission to the chain that represents an action that a proposer (either an
52//! account or an external origin) suggests that the system adopt.
53//! - **Referendum:** A proposal that is in the process of being voted on for either acceptance or
54//!   rejection as a change to the system.
55//! - **Delegation:** The act of granting your voting power to the decisions of another account for
56//!   up to a certain conviction.
57//!
58//! ### Adaptive Quorum Biasing
59//!
60//! A _referendum_ can be either simple majority-carries in which 50%+1 of the
61//! votes decide the outcome or _adaptive quorum biased_. Adaptive quorum biasing
62//! makes the threshold for passing or rejecting a referendum higher or lower
63//! depending on how the referendum was originally proposed. There are two types of
64//! adaptive quorum biasing: 1) _positive turnout bias_ makes a referendum
65//! require a super-majority to pass that decreases as turnout increases and
66//! 2) _negative turnout bias_ makes a referendum require a super-majority to
67//! reject that decreases as turnout increases. Another way to think about the
68//! quorum biasing is that _positive bias_ referendums will be rejected by
69//! default and _negative bias_ referendums get passed by default.
70//!
71//! ## Interface
72//!
73//! ### Dispatchable Functions
74//!
75//! #### Public
76//!
77//! These calls can be made from any externally held account capable of creating
78//! a signed extrinsic.
79//!
80//! Basic actions:
81//! - `propose` - Submits a sensitive action, represented as a hash. Requires a deposit.
82//! - `second` - Signals agreement with a proposal, moves it higher on the proposal queue, and
83//!   requires a matching deposit to the original.
84//! - `vote` - Votes in a referendum, either the vote is "Aye" to enact the proposal or "Nay" to
85//!   keep the status quo.
86//! - `unvote` - Cancel a previous vote, this must be done by the voter before the vote ends.
87//! - `delegate` - Delegates the voting power (tokens * conviction) to another account.
88//! - `undelegate` - Stops the delegation of voting power to another account.
89//!
90//! Administration actions that can be done to any account:
91//! - `reap_vote` - Remove some account's expired votes.
92//! - `unlock` - Redetermine the account's balance lock, potentially making tokens available.
93//!
94//! Preimage actions:
95//! - `note_preimage` - Registers the preimage for an upcoming proposal, requires a deposit that is
96//!   returned once the proposal is enacted.
97//! - `note_preimage_operational` - same but provided by `T::OperationalPreimageOrigin`.
98//! - `note_imminent_preimage` - Registers the preimage for an upcoming proposal. Does not require a
99//!   deposit, but the proposal must be in the dispatch queue.
100//! - `note_imminent_preimage_operational` - same but provided by `T::OperationalPreimageOrigin`.
101//! - `reap_preimage` - Removes the preimage for an expired proposal. Will only work under the
102//!   condition that it's the same account that noted it and after the voting period, OR it's a
103//!   different account after the enactment period.
104//!
105//! #### Cancellation Origin
106//!
107//! This call can only be made by the `CancellationOrigin`.
108//!
109//! - `emergency_cancel` - Schedules an emergency cancellation of a referendum. Can only happen once
110//!   to a specific referendum.
111//!
112//! #### ExternalOrigin
113//!
114//! This call can only be made by the `ExternalOrigin`.
115//!
116//! - `external_propose` - Schedules a proposal to become a referendum once it is legal for an
117//!   externally proposed referendum.
118//!
119//! #### External Majority Origin
120//!
121//! This call can only be made by the `ExternalMajorityOrigin`.
122//!
123//! - `external_propose_majority` - Schedules a proposal to become a majority-carries referendum
124//!   once it is legal for an externally proposed referendum.
125//!
126//! #### External Default Origin
127//!
128//! This call can only be made by the `ExternalDefaultOrigin`.
129//!
130//! - `external_propose_default` - Schedules a proposal to become a negative-turnout-bias referendum
131//!   once it is legal for an externally proposed referendum.
132//!
133//! #### Fast Track Origin
134//!
135//! This call can only be made by the `FastTrackOrigin`.
136//!
137//! - `fast_track` - Schedules the current externally proposed proposal that is "majority-carries"
138//!   to become a referendum immediately.
139//!
140//! #### Veto Origin
141//!
142//! This call can only be made by the `VetoOrigin`.
143//!
144//! - `veto_external` - Vetoes and blacklists the external proposal hash.
145//!
146//! #### Root
147//!
148//! - `cancel_referendum` - Removes a referendum.
149//! - `cancel_queued` - Cancels a proposal that is queued for enactment.
150//! - `clear_public_proposal` - Removes all public proposals.
151
152#![recursion_limit = "256"]
153#![cfg_attr(not(feature = "std"), no_std)]
154
155extern crate alloc;
156
157use alloc::{vec, vec::Vec};
158use codec::{Decode, Encode};
159use pezframe_support::{
160	ensure,
161	traits::{
162		defensive_prelude::*,
163		schedule::{v3::Named as ScheduleNamed, DispatchTime},
164		Bounded, Currency, EnsureOrigin, Get, LockIdentifier, LockableCurrency, OnUnbalanced,
165		QueryPreimage, ReservableCurrency, StorePreimage, WithdrawReasons,
166	},
167	weights::Weight,
168};
169use pezframe_system::pezpallet_prelude::{BlockNumberFor, OriginFor};
170use pezsp_runtime::{
171	traits::{BadOrigin, Bounded as ArithBounded, One, Saturating, StaticLookup, Zero},
172	ArithmeticError, DispatchError, DispatchResult,
173};
174
175mod conviction;
176mod types;
177mod vote;
178mod vote_threshold;
179pub mod weights;
180pub use conviction::Conviction;
181pub use pezpallet::*;
182pub use types::{
183	Delegations, MetadataOwner, PropIndex, ReferendumIndex, ReferendumInfo, ReferendumStatus,
184	Tally, UnvoteScope,
185};
186pub use vote::{AccountVote, Vote, Voting};
187pub use vote_threshold::{Approved, VoteThreshold};
188pub use weights::WeightInfo;
189
190#[cfg(test)]
191mod tests;
192
193#[cfg(feature = "runtime-benchmarks")]
194pub mod benchmarking;
195
196pub mod migrations;
197
198pub(crate) const DEMOCRACY_ID: LockIdentifier = *b"democrac";
199
200type BalanceOf<T> =
201	<<T as Config>::Currency as Currency<<T as pezframe_system::Config>::AccountId>>::Balance;
202type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
203	<T as pezframe_system::Config>::AccountId,
204>>::NegativeImbalance;
205pub type CallOf<T> = <T as pezframe_system::Config>::RuntimeCall;
206pub type BoundedCallOf<T> = Bounded<CallOf<T>, <T as pezframe_system::Config>::Hashing>;
207type AccountIdLookupOf<T> = <<T as pezframe_system::Config>::Lookup as StaticLookup>::Source;
208
209#[pezframe_support::pezpallet]
210pub mod pezpallet {
211	use super::{DispatchResult, *};
212	use pezframe_support::pezpallet_prelude::*;
213	use pezframe_system::pezpallet_prelude::*;
214
215	/// The in-code storage version.
216	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
217
218	#[pezpallet::pezpallet]
219	#[pezpallet::storage_version(STORAGE_VERSION)]
220	pub struct Pezpallet<T>(_);
221
222	#[pezpallet::config]
223	pub trait Config: pezframe_system::Config + Sized {
224		type WeightInfo: WeightInfo;
225		#[allow(deprecated)]
226		type RuntimeEvent: From<Event<Self>>
227			+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
228
229		/// The Scheduler.
230		type Scheduler: ScheduleNamed<
231			BlockNumberFor<Self>,
232			CallOf<Self>,
233			Self::PalletsOrigin,
234			Hasher = Self::Hashing,
235		>;
236
237		/// The Preimage provider.
238		type Preimages: QueryPreimage<H = Self::Hashing> + StorePreimage;
239
240		/// Currency type for this pezpallet.
241		type Currency: ReservableCurrency<Self::AccountId>
242			+ LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
243
244		/// The period between a proposal being approved and enacted.
245		///
246		/// It should generally be a little more than the unstake period to ensure that
247		/// voting stakers have an opportunity to remove themselves from the system in the case
248		/// where they are on the losing side of a vote.
249		#[pezpallet::constant]
250		type EnactmentPeriod: Get<BlockNumberFor<Self>>;
251
252		/// How often (in blocks) new public referenda are launched.
253		#[pezpallet::constant]
254		type LaunchPeriod: Get<BlockNumberFor<Self>>;
255
256		/// How often (in blocks) to check for new votes.
257		#[pezpallet::constant]
258		type VotingPeriod: Get<BlockNumberFor<Self>>;
259
260		/// The minimum period of vote locking.
261		///
262		/// It should be no shorter than enactment period to ensure that in the case of an approval,
263		/// those successful voters are locked into the consequences that their votes entail.
264		#[pezpallet::constant]
265		type VoteLockingPeriod: Get<BlockNumberFor<Self>>;
266
267		/// The minimum amount to be used as a deposit for a public referendum proposal.
268		#[pezpallet::constant]
269		type MinimumDeposit: Get<BalanceOf<Self>>;
270
271		/// Indicator for whether an emergency origin is even allowed to happen. Some chains may
272		/// want to set this permanently to `false`, others may want to condition it on things such
273		/// as an upgrade having happened recently.
274		#[pezpallet::constant]
275		type InstantAllowed: Get<bool>;
276
277		/// Minimum voting period allowed for a fast-track referendum.
278		#[pezpallet::constant]
279		type FastTrackVotingPeriod: Get<BlockNumberFor<Self>>;
280
281		/// Period in blocks where an external proposal may not be re-submitted after being vetoed.
282		#[pezpallet::constant]
283		type CooloffPeriod: Get<BlockNumberFor<Self>>;
284
285		/// The maximum number of votes for an account.
286		///
287		/// Also used to compute weight, an overly big value can
288		/// lead to extrinsic with very big weight: see `delegate` for instance.
289		#[pezpallet::constant]
290		type MaxVotes: Get<u32>;
291
292		/// The maximum number of public proposals that can exist at any time.
293		#[pezpallet::constant]
294		type MaxProposals: Get<u32>;
295
296		/// The maximum number of deposits a public proposal may have at any time.
297		#[pezpallet::constant]
298		type MaxDeposits: Get<u32>;
299
300		/// The maximum number of items which can be blacklisted.
301		#[pezpallet::constant]
302		type MaxBlacklisted: Get<u32>;
303
304		/// Origin from which the next tabled referendum may be forced. This is a normal
305		/// "super-majority-required" referendum.
306		type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
307
308		/// Origin from which the next tabled referendum may be forced; this allows for the tabling
309		/// of a majority-carries referendum.
310		type ExternalMajorityOrigin: EnsureOrigin<Self::RuntimeOrigin>;
311
312		/// Origin from which the next tabled referendum may be forced; this allows for the tabling
313		/// of a negative-turnout-bias (default-carries) referendum.
314		type ExternalDefaultOrigin: EnsureOrigin<Self::RuntimeOrigin>;
315
316		/// Origin from which the new proposal can be made.
317		///
318		/// The success variant is the account id of the depositor.
319		type SubmitOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
320
321		/// Origin from which the next majority-carries (or more permissive) referendum may be
322		/// tabled to vote according to the `FastTrackVotingPeriod` asynchronously in a similar
323		/// manner to the emergency origin. It retains its threshold method.
324		type FastTrackOrigin: EnsureOrigin<Self::RuntimeOrigin>;
325
326		/// Origin from which the next majority-carries (or more permissive) referendum may be
327		/// tabled to vote immediately and asynchronously in a similar manner to the emergency
328		/// origin. It retains its threshold method.
329		type InstantOrigin: EnsureOrigin<Self::RuntimeOrigin>;
330
331		/// Origin from which any referendum may be cancelled in an emergency.
332		type CancellationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
333
334		/// Origin from which proposals may be blacklisted.
335		type BlacklistOrigin: EnsureOrigin<Self::RuntimeOrigin>;
336
337		/// Origin from which a proposal may be cancelled and its backers slashed.
338		type CancelProposalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
339
340		/// Origin for anyone able to veto proposals.
341		type VetoOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
342
343		/// Overarching type of all pallets origins.
344		type PalletsOrigin: From<pezframe_system::RawOrigin<Self::AccountId>>;
345
346		/// Handler for the unbalanced reduction when slashing a preimage deposit.
347		type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
348	}
349
350	/// The number of (public) proposals that have been made so far.
351	#[pezpallet::storage]
352	pub type PublicPropCount<T> = StorageValue<_, PropIndex, ValueQuery>;
353
354	/// The public proposals. Unsorted. The second item is the proposal.
355	#[pezpallet::storage]
356	pub type PublicProps<T: Config> = StorageValue<
357		_,
358		BoundedVec<(PropIndex, BoundedCallOf<T>, T::AccountId), T::MaxProposals>,
359		ValueQuery,
360	>;
361
362	/// Those who have locked a deposit.
363	///
364	/// TWOX-NOTE: Safe, as increasing integer keys are safe.
365	#[pezpallet::storage]
366	pub type DepositOf<T: Config> = StorageMap<
367		_,
368		Twox64Concat,
369		PropIndex,
370		(BoundedVec<T::AccountId, T::MaxDeposits>, BalanceOf<T>),
371	>;
372
373	/// The next free referendum index, aka the number of referenda started so far.
374	#[pezpallet::storage]
375	pub type ReferendumCount<T> = StorageValue<_, ReferendumIndex, ValueQuery>;
376
377	/// The lowest referendum index representing an unbaked referendum. Equal to
378	/// `ReferendumCount` if there isn't a unbaked referendum.
379	#[pezpallet::storage]
380	pub type LowestUnbaked<T> = StorageValue<_, ReferendumIndex, ValueQuery>;
381
382	/// Information concerning any given referendum.
383	///
384	/// TWOX-NOTE: SAFE as indexes are not under an attacker’s control.
385	#[pezpallet::storage]
386	pub type ReferendumInfoOf<T: Config> = StorageMap<
387		_,
388		Twox64Concat,
389		ReferendumIndex,
390		ReferendumInfo<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>,
391	>;
392
393	/// All votes for a particular voter. We store the balance for the number of votes that we
394	/// have recorded. The second item is the total amount of delegations, that will be added.
395	///
396	/// TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway.
397	#[pezpallet::storage]
398	pub type VotingOf<T: Config> = StorageMap<
399		_,
400		Twox64Concat,
401		T::AccountId,
402		Voting<BalanceOf<T>, T::AccountId, BlockNumberFor<T>, T::MaxVotes>,
403		ValueQuery,
404	>;
405
406	/// True if the last referendum tabled was submitted externally. False if it was a public
407	/// proposal.
408	#[pezpallet::storage]
409	pub type LastTabledWasExternal<T> = StorageValue<_, bool, ValueQuery>;
410
411	/// The referendum to be tabled whenever it would be valid to table an external proposal.
412	/// This happens when a referendum needs to be tabled and one of two conditions are met:
413	/// - `LastTabledWasExternal` is `false`; or
414	/// - `PublicProps` is empty.
415	#[pezpallet::storage]
416	pub type NextExternal<T: Config> = StorageValue<_, (BoundedCallOf<T>, VoteThreshold)>;
417
418	/// A record of who vetoed what. Maps proposal hash to a possible existent block number
419	/// (until when it may not be resubmitted) and who vetoed it.
420	#[pezpallet::storage]
421	pub type Blacklist<T: Config> = StorageMap<
422		_,
423		Identity,
424		T::Hash,
425		(BlockNumberFor<T>, BoundedVec<T::AccountId, T::MaxBlacklisted>),
426	>;
427
428	/// Record of all proposals that have been subject to emergency cancellation.
429	#[pezpallet::storage]
430	pub type Cancellations<T: Config> = StorageMap<_, Identity, T::Hash, bool, ValueQuery>;
431
432	/// General information concerning any proposal or referendum.
433	/// The `Hash` refers to the preimage of the `Preimages` provider which can be a JSON
434	/// dump or IPFS hash of a JSON file.
435	///
436	/// Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove)
437	/// large preimages.
438	#[pezpallet::storage]
439	pub type MetadataOf<T: Config> = StorageMap<_, Blake2_128Concat, MetadataOwner, T::Hash>;
440
441	#[pezpallet::genesis_config]
442	#[derive(pezframe_support::DefaultNoBound)]
443	pub struct GenesisConfig<T: Config> {
444		#[serde(skip)]
445		_config: core::marker::PhantomData<T>,
446	}
447
448	#[pezpallet::genesis_build]
449	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
450		fn build(&self) {
451			PublicPropCount::<T>::put(0 as PropIndex);
452			ReferendumCount::<T>::put(0 as ReferendumIndex);
453			LowestUnbaked::<T>::put(0 as ReferendumIndex);
454		}
455	}
456
457	#[pezpallet::event]
458	#[pezpallet::generate_deposit(pub(super) fn deposit_event)]
459	pub enum Event<T: Config> {
460		/// A motion has been proposed by a public account.
461		Proposed { proposal_index: PropIndex, deposit: BalanceOf<T> },
462		/// A public proposal has been tabled for referendum vote.
463		Tabled { proposal_index: PropIndex, deposit: BalanceOf<T> },
464		/// An external proposal has been tabled.
465		ExternalTabled,
466		/// A referendum has begun.
467		Started { ref_index: ReferendumIndex, threshold: VoteThreshold },
468		/// A proposal has been approved by referendum.
469		Passed { ref_index: ReferendumIndex },
470		/// A proposal has been rejected by referendum.
471		NotPassed { ref_index: ReferendumIndex },
472		/// A referendum has been cancelled.
473		Cancelled { ref_index: ReferendumIndex },
474		/// An account has delegated their vote to another account.
475		Delegated { who: T::AccountId, target: T::AccountId },
476		/// An account has cancelled a previous delegation operation.
477		Undelegated { account: T::AccountId },
478		/// An external proposal has been vetoed.
479		Vetoed { who: T::AccountId, proposal_hash: T::Hash, until: BlockNumberFor<T> },
480		/// A proposal_hash has been blacklisted permanently.
481		Blacklisted { proposal_hash: T::Hash },
482		/// An account has voted in a referendum
483		Voted { voter: T::AccountId, ref_index: ReferendumIndex, vote: AccountVote<BalanceOf<T>> },
484		/// An account has seconded a proposal
485		Seconded { seconder: T::AccountId, prop_index: PropIndex },
486		/// A proposal got canceled.
487		ProposalCanceled { prop_index: PropIndex },
488		/// Metadata for a proposal or a referendum has been set.
489		MetadataSet {
490			/// Metadata owner.
491			owner: MetadataOwner,
492			/// Preimage hash.
493			hash: T::Hash,
494		},
495		/// Metadata for a proposal or a referendum has been cleared.
496		MetadataCleared {
497			/// Metadata owner.
498			owner: MetadataOwner,
499			/// Preimage hash.
500			hash: T::Hash,
501		},
502		/// Metadata has been transferred to new owner.
503		MetadataTransferred {
504			/// Previous metadata owner.
505			prev_owner: MetadataOwner,
506			/// New metadata owner.
507			owner: MetadataOwner,
508			/// Preimage hash.
509			hash: T::Hash,
510		},
511	}
512
513	#[pezpallet::error]
514	pub enum Error<T> {
515		/// Value too low
516		ValueLow,
517		/// Proposal does not exist
518		ProposalMissing,
519		/// Cannot cancel the same proposal twice
520		AlreadyCanceled,
521		/// Proposal already made
522		DuplicateProposal,
523		/// Proposal still blacklisted
524		ProposalBlacklisted,
525		/// Next external proposal not simple majority
526		NotSimpleMajority,
527		/// Invalid hash
528		InvalidHash,
529		/// No external proposal
530		NoProposal,
531		/// Identity may not veto a proposal twice
532		AlreadyVetoed,
533		/// Vote given for invalid referendum
534		ReferendumInvalid,
535		/// No proposals waiting
536		NoneWaiting,
537		/// The given account did not vote on the referendum.
538		NotVoter,
539		/// The actor has no permission to conduct the action.
540		NoPermission,
541		/// The account is already delegating.
542		AlreadyDelegating,
543		/// Too high a balance was provided that the account cannot afford.
544		InsufficientFunds,
545		/// The account is not currently delegating.
546		NotDelegating,
547		/// The account currently has votes attached to it and the operation cannot succeed until
548		/// these are removed, either through `unvote` or `reap_vote`.
549		VotesExist,
550		/// The instant referendum origin is currently disallowed.
551		InstantNotAllowed,
552		/// Delegation to oneself makes no sense.
553		Nonsense,
554		/// Invalid upper bound.
555		WrongUpperBound,
556		/// Maximum number of votes reached.
557		MaxVotesReached,
558		/// Maximum number of items reached.
559		TooMany,
560		/// Voting period too low
561		VotingPeriodLow,
562		/// The preimage does not exist.
563		PreimageNotExist,
564	}
565
566	#[pezpallet::hooks]
567	impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
568		/// Weight: see `begin_block`
569		fn on_initialize(n: BlockNumberFor<T>) -> Weight {
570			Self::begin_block(n)
571		}
572	}
573
574	#[pezpallet::call]
575	impl<T: Config> Pezpallet<T> {
576		/// Propose a sensitive action to be taken.
577		///
578		/// The dispatch origin of this call must be _Signed_ and the sender must
579		/// have funds to cover the deposit.
580		///
581		/// - `proposal_hash`: The hash of the proposal preimage.
582		/// - `value`: The amount of deposit (must be at least `MinimumDeposit`).
583		///
584		/// Emits `Proposed`.
585		#[pezpallet::call_index(0)]
586		#[pezpallet::weight(T::WeightInfo::propose())]
587		pub fn propose(
588			origin: OriginFor<T>,
589			proposal: BoundedCallOf<T>,
590			#[pezpallet::compact] value: BalanceOf<T>,
591		) -> DispatchResult {
592			let who = T::SubmitOrigin::ensure_origin(origin)?;
593			ensure!(value >= T::MinimumDeposit::get(), Error::<T>::ValueLow);
594
595			let index = PublicPropCount::<T>::get();
596			let real_prop_count = PublicProps::<T>::decode_len().unwrap_or(0) as u32;
597			let max_proposals = T::MaxProposals::get();
598			ensure!(real_prop_count < max_proposals, Error::<T>::TooMany);
599			let proposal_hash = proposal.hash();
600
601			if let Some((until, _)) = Blacklist::<T>::get(proposal_hash) {
602				ensure!(
603					pezframe_system::Pezpallet::<T>::block_number() >= until,
604					Error::<T>::ProposalBlacklisted,
605				);
606			}
607
608			T::Currency::reserve(&who, value)?;
609
610			let depositors = BoundedVec::<_, T::MaxDeposits>::truncate_from(vec![who.clone()]);
611			DepositOf::<T>::insert(index, (depositors, value));
612
613			PublicPropCount::<T>::put(index + 1);
614
615			PublicProps::<T>::try_append((index, proposal, who))
616				.map_err(|_| Error::<T>::TooMany)?;
617
618			Self::deposit_event(Event::<T>::Proposed { proposal_index: index, deposit: value });
619			Ok(())
620		}
621
622		/// Signals agreement with a particular proposal.
623		///
624		/// The dispatch origin of this call must be _Signed_ and the sender
625		/// must have funds to cover the deposit, equal to the original deposit.
626		///
627		/// - `proposal`: The index of the proposal to second.
628		#[pezpallet::call_index(1)]
629		#[pezpallet::weight(T::WeightInfo::second())]
630		pub fn second(
631			origin: OriginFor<T>,
632			#[pezpallet::compact] proposal: PropIndex,
633		) -> DispatchResult {
634			let who = ensure_signed(origin)?;
635
636			let seconds = Self::len_of_deposit_of(proposal).ok_or(Error::<T>::ProposalMissing)?;
637			ensure!(seconds < T::MaxDeposits::get(), Error::<T>::TooMany);
638			let mut deposit = DepositOf::<T>::get(proposal).ok_or(Error::<T>::ProposalMissing)?;
639			T::Currency::reserve(&who, deposit.1)?;
640			let ok = deposit.0.try_push(who.clone()).is_ok();
641			debug_assert!(ok, "`seconds` is below static limit; `try_insert` should succeed; qed");
642			DepositOf::<T>::insert(proposal, deposit);
643			Self::deposit_event(Event::<T>::Seconded { seconder: who, prop_index: proposal });
644			Ok(())
645		}
646
647		/// Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;
648		/// otherwise it is a vote to keep the status quo.
649		///
650		/// The dispatch origin of this call must be _Signed_.
651		///
652		/// - `ref_index`: The index of the referendum to vote for.
653		/// - `vote`: The vote configuration.
654		#[pezpallet::call_index(2)]
655		#[pezpallet::weight(T::WeightInfo::vote_new().max(T::WeightInfo::vote_existing()))]
656		pub fn vote(
657			origin: OriginFor<T>,
658			#[pezpallet::compact] ref_index: ReferendumIndex,
659			vote: AccountVote<BalanceOf<T>>,
660		) -> DispatchResult {
661			let who = ensure_signed(origin)?;
662			Self::try_vote(&who, ref_index, vote)
663		}
664
665		/// Schedule an emergency cancellation of a referendum. Cannot happen twice to the same
666		/// referendum.
667		///
668		/// The dispatch origin of this call must be `CancellationOrigin`.
669		///
670		/// -`ref_index`: The index of the referendum to cancel.
671		///
672		/// Weight: `O(1)`.
673		#[pezpallet::call_index(3)]
674		#[pezpallet::weight((T::WeightInfo::emergency_cancel(), DispatchClass::Operational))]
675		pub fn emergency_cancel(
676			origin: OriginFor<T>,
677			ref_index: ReferendumIndex,
678		) -> DispatchResult {
679			T::CancellationOrigin::ensure_origin(origin)?;
680
681			let status = Self::referendum_status(ref_index)?;
682			let h = status.proposal.hash();
683			ensure!(!Cancellations::<T>::contains_key(h), Error::<T>::AlreadyCanceled);
684
685			Cancellations::<T>::insert(h, true);
686			Self::internal_cancel_referendum(ref_index);
687			Ok(())
688		}
689
690		/// Schedule a referendum to be tabled once it is legal to schedule an external
691		/// referendum.
692		///
693		/// The dispatch origin of this call must be `ExternalOrigin`.
694		///
695		/// - `proposal_hash`: The preimage hash of the proposal.
696		#[pezpallet::call_index(4)]
697		#[pezpallet::weight(T::WeightInfo::external_propose())]
698		pub fn external_propose(
699			origin: OriginFor<T>,
700			proposal: BoundedCallOf<T>,
701		) -> DispatchResult {
702			T::ExternalOrigin::ensure_origin(origin)?;
703			ensure!(!NextExternal::<T>::exists(), Error::<T>::DuplicateProposal);
704			if let Some((until, _)) = Blacklist::<T>::get(proposal.hash()) {
705				ensure!(
706					pezframe_system::Pezpallet::<T>::block_number() >= until,
707					Error::<T>::ProposalBlacklisted,
708				);
709			}
710			NextExternal::<T>::put((proposal, VoteThreshold::SuperMajorityApprove));
711			Ok(())
712		}
713
714		/// Schedule a majority-carries referendum to be tabled next once it is legal to schedule
715		/// an external referendum.
716		///
717		/// The dispatch of this call must be `ExternalMajorityOrigin`.
718		///
719		/// - `proposal_hash`: The preimage hash of the proposal.
720		///
721		/// Unlike `external_propose`, blacklisting has no effect on this and it may replace a
722		/// pre-scheduled `external_propose` call.
723		///
724		/// Weight: `O(1)`
725		#[pezpallet::call_index(5)]
726		#[pezpallet::weight(T::WeightInfo::external_propose_majority())]
727		pub fn external_propose_majority(
728			origin: OriginFor<T>,
729			proposal: BoundedCallOf<T>,
730		) -> DispatchResult {
731			T::ExternalMajorityOrigin::ensure_origin(origin)?;
732			NextExternal::<T>::put((proposal, VoteThreshold::SimpleMajority));
733			Ok(())
734		}
735
736		/// Schedule a negative-turnout-bias referendum to be tabled next once it is legal to
737		/// schedule an external referendum.
738		///
739		/// The dispatch of this call must be `ExternalDefaultOrigin`.
740		///
741		/// - `proposal_hash`: The preimage hash of the proposal.
742		///
743		/// Unlike `external_propose`, blacklisting has no effect on this and it may replace a
744		/// pre-scheduled `external_propose` call.
745		///
746		/// Weight: `O(1)`
747		#[pezpallet::call_index(6)]
748		#[pezpallet::weight(T::WeightInfo::external_propose_default())]
749		pub fn external_propose_default(
750			origin: OriginFor<T>,
751			proposal: BoundedCallOf<T>,
752		) -> DispatchResult {
753			T::ExternalDefaultOrigin::ensure_origin(origin)?;
754			NextExternal::<T>::put((proposal, VoteThreshold::SuperMajorityAgainst));
755			Ok(())
756		}
757
758		/// Schedule the currently externally-proposed majority-carries referendum to be tabled
759		/// immediately. If there is no externally-proposed referendum currently, or if there is one
760		/// but it is not a majority-carries referendum then it fails.
761		///
762		/// The dispatch of this call must be `FastTrackOrigin`.
763		///
764		/// - `proposal_hash`: The hash of the current external proposal.
765		/// - `voting_period`: The period that is allowed for voting on this proposal. Increased to
766		/// 	Must be always greater than zero.
767		/// 	For `FastTrackOrigin` must be equal or greater than `FastTrackVotingPeriod`.
768		/// - `delay`: The number of block after voting has ended in approval and this should be
769		///   enacted. This doesn't have a minimum amount.
770		///
771		/// Emits `Started`.
772		///
773		/// Weight: `O(1)`
774		#[pezpallet::call_index(7)]
775		#[pezpallet::weight(T::WeightInfo::fast_track())]
776		pub fn fast_track(
777			origin: OriginFor<T>,
778			proposal_hash: T::Hash,
779			voting_period: BlockNumberFor<T>,
780			delay: BlockNumberFor<T>,
781		) -> DispatchResult {
782			// Rather complicated bit of code to ensure that either:
783			// - `voting_period` is at least `FastTrackVotingPeriod` and `origin` is
784			//   `FastTrackOrigin`; or
785			// - `InstantAllowed` is `true` and `origin` is `InstantOrigin`.
786			let maybe_ensure_instant = if voting_period < T::FastTrackVotingPeriod::get() {
787				Some(origin)
788			} else {
789				T::FastTrackOrigin::try_origin(origin).err()
790			};
791			if let Some(ensure_instant) = maybe_ensure_instant {
792				T::InstantOrigin::ensure_origin(ensure_instant)?;
793				ensure!(T::InstantAllowed::get(), Error::<T>::InstantNotAllowed);
794			}
795
796			ensure!(voting_period > Zero::zero(), Error::<T>::VotingPeriodLow);
797			let (ext_proposal, threshold) =
798				NextExternal::<T>::get().ok_or(Error::<T>::ProposalMissing)?;
799			ensure!(
800				threshold != VoteThreshold::SuperMajorityApprove,
801				Error::<T>::NotSimpleMajority,
802			);
803			ensure!(proposal_hash == ext_proposal.hash(), Error::<T>::InvalidHash);
804
805			NextExternal::<T>::kill();
806			let now = pezframe_system::Pezpallet::<T>::block_number();
807			let ref_index = Self::inject_referendum(
808				now.saturating_add(voting_period),
809				ext_proposal,
810				threshold,
811				delay,
812			);
813			Self::transfer_metadata(MetadataOwner::External, MetadataOwner::Referendum(ref_index));
814			Ok(())
815		}
816
817		/// Veto and blacklist the external proposal hash.
818		///
819		/// The dispatch origin of this call must be `VetoOrigin`.
820		///
821		/// - `proposal_hash`: The preimage hash of the proposal to veto and blacklist.
822		///
823		/// Emits `Vetoed`.
824		///
825		/// Weight: `O(V + log(V))` where V is number of `existing vetoers`
826		#[pezpallet::call_index(8)]
827		#[pezpallet::weight(T::WeightInfo::veto_external())]
828		pub fn veto_external(origin: OriginFor<T>, proposal_hash: T::Hash) -> DispatchResult {
829			let who = T::VetoOrigin::ensure_origin(origin)?;
830
831			if let Some((ext_proposal, _)) = NextExternal::<T>::get() {
832				ensure!(proposal_hash == ext_proposal.hash(), Error::<T>::ProposalMissing);
833			} else {
834				return Err(Error::<T>::NoProposal.into());
835			}
836
837			let mut existing_vetoers =
838				Blacklist::<T>::get(&proposal_hash).map(|pair| pair.1).unwrap_or_default();
839			let insert_position =
840				existing_vetoers.binary_search(&who).err().ok_or(Error::<T>::AlreadyVetoed)?;
841			existing_vetoers
842				.try_insert(insert_position, who.clone())
843				.map_err(|_| Error::<T>::TooMany)?;
844
845			let until = pezframe_system::Pezpallet::<T>::block_number()
846				.saturating_add(T::CooloffPeriod::get());
847			Blacklist::<T>::insert(&proposal_hash, (until, existing_vetoers));
848
849			Self::deposit_event(Event::<T>::Vetoed { who, proposal_hash, until });
850			NextExternal::<T>::kill();
851			Self::clear_metadata(MetadataOwner::External);
852			Ok(())
853		}
854
855		/// Remove a referendum.
856		///
857		/// The dispatch origin of this call must be _Root_.
858		///
859		/// - `ref_index`: The index of the referendum to cancel.
860		///
861		/// # Weight: `O(1)`.
862		#[pezpallet::call_index(9)]
863		#[pezpallet::weight(T::WeightInfo::cancel_referendum())]
864		pub fn cancel_referendum(
865			origin: OriginFor<T>,
866			#[pezpallet::compact] ref_index: ReferendumIndex,
867		) -> DispatchResult {
868			ensure_root(origin)?;
869			Self::internal_cancel_referendum(ref_index);
870			Ok(())
871		}
872
873		/// Delegate the voting power (with some given conviction) of the sending account.
874		///
875		/// The balance delegated is locked for as long as it's delegated, and thereafter for the
876		/// time appropriate for the conviction's lock period.
877		///
878		/// The dispatch origin of this call must be _Signed_, and the signing account must either:
879		///   - be delegating already; or
880		///   - have no voting activity (if there is, then it will need to be removed/consolidated
881		///     through `reap_vote` or `unvote`).
882		///
883		/// - `to`: The account whose voting the `target` account's voting power will follow.
884		/// - `conviction`: The conviction that will be attached to the delegated votes. When the
885		///   account is undelegated, the funds will be locked for the corresponding period.
886		/// - `balance`: The amount of the account's balance to be used in delegating. This must not
887		///   be more than the account's current balance.
888		///
889		/// Emits `Delegated`.
890		///
891		/// Weight: `O(R)` where R is the number of referendums the voter delegating to has
892		///   voted on. Weight is charged as if maximum votes.
893		// NOTE: weight must cover an incorrect voting of origin with max votes, this is ensure
894		// because a valid delegation cover decoding a direct voting with max votes.
895		#[pezpallet::call_index(10)]
896		#[pezpallet::weight(T::WeightInfo::delegate(T::MaxVotes::get()))]
897		pub fn delegate(
898			origin: OriginFor<T>,
899			to: AccountIdLookupOf<T>,
900			conviction: Conviction,
901			balance: BalanceOf<T>,
902		) -> DispatchResultWithPostInfo {
903			let who = ensure_signed(origin)?;
904			let to = T::Lookup::lookup(to)?;
905			let votes = Self::try_delegate(who, to, conviction, balance)?;
906
907			Ok(Some(T::WeightInfo::delegate(votes)).into())
908		}
909
910		/// Undelegate the voting power of the sending account.
911		///
912		/// Tokens may be unlocked following once an amount of time consistent with the lock period
913		/// of the conviction with which the delegation was issued.
914		///
915		/// The dispatch origin of this call must be _Signed_ and the signing account must be
916		/// currently delegating.
917		///
918		/// Emits `Undelegated`.
919		///
920		/// Weight: `O(R)` where R is the number of referendums the voter delegating to has
921		///   voted on. Weight is charged as if maximum votes.
922		// NOTE: weight must cover an incorrect voting of origin with max votes, this is ensure
923		// because a valid delegation cover decoding a direct voting with max votes.
924		#[pezpallet::call_index(11)]
925		#[pezpallet::weight(T::WeightInfo::undelegate(T::MaxVotes::get()))]
926		pub fn undelegate(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
927			let who = ensure_signed(origin)?;
928			let votes = Self::try_undelegate(who)?;
929			Ok(Some(T::WeightInfo::undelegate(votes)).into())
930		}
931
932		/// Clears all public proposals.
933		///
934		/// The dispatch origin of this call must be _Root_.
935		///
936		/// Weight: `O(1)`.
937		#[pezpallet::call_index(12)]
938		#[pezpallet::weight(T::WeightInfo::clear_public_proposals())]
939		pub fn clear_public_proposals(origin: OriginFor<T>) -> DispatchResult {
940			ensure_root(origin)?;
941			PublicProps::<T>::kill();
942			Ok(())
943		}
944
945		/// Unlock tokens that have an expired lock.
946		///
947		/// The dispatch origin of this call must be _Signed_.
948		///
949		/// - `target`: The account to remove the lock on.
950		///
951		/// Weight: `O(R)` with R number of vote of target.
952		#[pezpallet::call_index(13)]
953		#[pezpallet::weight(T::WeightInfo::unlock_set(T::MaxVotes::get()).max(T::WeightInfo::unlock_remove(T::MaxVotes::get())))]
954		pub fn unlock(origin: OriginFor<T>, target: AccountIdLookupOf<T>) -> DispatchResult {
955			ensure_signed(origin)?;
956			let target = T::Lookup::lookup(target)?;
957			Self::update_lock(&target);
958			Ok(())
959		}
960
961		/// Remove a vote for a referendum.
962		///
963		/// If:
964		/// - the referendum was cancelled, or
965		/// - the referendum is ongoing, or
966		/// - the referendum has ended such that
967		///   - the vote of the account was in opposition to the result; or
968		///   - there was no conviction to the account's vote; or
969		///   - the account made a split vote
970		/// ...then the vote is removed cleanly and a following call to `unlock` may result in more
971		/// funds being available.
972		///
973		/// If, however, the referendum has ended and:
974		/// - it finished corresponding to the vote of the account, and
975		/// - the account made a standard vote with conviction, and
976		/// - the lock period of the conviction is not over
977		/// ...then the lock will be aggregated into the overall account's lock, which may involve
978		/// *overlocking* (where the two locks are combined into a single lock that is the maximum
979		/// of both the amount locked and the time is it locked for).
980		///
981		/// The dispatch origin of this call must be _Signed_, and the signer must have a vote
982		/// registered for referendum `index`.
983		///
984		/// - `index`: The index of referendum of the vote to be removed.
985		///
986		/// Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on.
987		///   Weight is calculated for the maximum number of vote.
988		#[pezpallet::call_index(14)]
989		#[pezpallet::weight(T::WeightInfo::remove_vote(T::MaxVotes::get()))]
990		pub fn remove_vote(origin: OriginFor<T>, index: ReferendumIndex) -> DispatchResult {
991			let who = ensure_signed(origin)?;
992			Self::try_remove_vote(&who, index, UnvoteScope::Any)
993		}
994
995		/// Remove a vote for a referendum.
996		///
997		/// If the `target` is equal to the signer, then this function is exactly equivalent to
998		/// `remove_vote`. If not equal to the signer, then the vote must have expired,
999		/// either because the referendum was cancelled, because the voter lost the referendum or
1000		/// because the conviction period is over.
1001		///
1002		/// The dispatch origin of this call must be _Signed_.
1003		///
1004		/// - `target`: The account of the vote to be removed; this account must have voted for
1005		///   referendum `index`.
1006		/// - `index`: The index of referendum of the vote to be removed.
1007		///
1008		/// Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on.
1009		///   Weight is calculated for the maximum number of vote.
1010		#[pezpallet::call_index(15)]
1011		#[pezpallet::weight(T::WeightInfo::remove_other_vote(T::MaxVotes::get()))]
1012		pub fn remove_other_vote(
1013			origin: OriginFor<T>,
1014			target: AccountIdLookupOf<T>,
1015			index: ReferendumIndex,
1016		) -> DispatchResult {
1017			let who = ensure_signed(origin)?;
1018			let target = T::Lookup::lookup(target)?;
1019			let scope = if target == who { UnvoteScope::Any } else { UnvoteScope::OnlyExpired };
1020			Self::try_remove_vote(&target, index, scope)?;
1021			Ok(())
1022		}
1023
1024		/// Permanently place a proposal into the blacklist. This prevents it from ever being
1025		/// proposed again.
1026		///
1027		/// If called on a queued public or external proposal, then this will result in it being
1028		/// removed. If the `ref_index` supplied is an active referendum with the proposal hash,
1029		/// then it will be cancelled.
1030		///
1031		/// The dispatch origin of this call must be `BlacklistOrigin`.
1032		///
1033		/// - `proposal_hash`: The proposal hash to blacklist permanently.
1034		/// - `ref_index`: An ongoing referendum whose hash is `proposal_hash`, which will be
1035		/// cancelled.
1036		///
1037		/// Weight: `O(p)` (though as this is an high-privilege dispatch, we assume it has a
1038		///   reasonable value).
1039		#[pezpallet::call_index(16)]
1040		#[pezpallet::weight((T::WeightInfo::blacklist(), DispatchClass::Operational))]
1041		pub fn blacklist(
1042			origin: OriginFor<T>,
1043			proposal_hash: T::Hash,
1044			maybe_ref_index: Option<ReferendumIndex>,
1045		) -> DispatchResult {
1046			T::BlacklistOrigin::ensure_origin(origin)?;
1047
1048			// Insert the proposal into the blacklist.
1049			let permanent =
1050				(BlockNumberFor::<T>::max_value(), BoundedVec::<T::AccountId, _>::default());
1051			Blacklist::<T>::insert(&proposal_hash, permanent);
1052
1053			// Remove the queued proposal, if it's there.
1054			PublicProps::<T>::mutate(|props| {
1055				if let Some(index) = props.iter().position(|p| p.1.hash() == proposal_hash) {
1056					let (prop_index, ..) = props.remove(index);
1057					if let Some((whos, amount)) = DepositOf::<T>::take(prop_index) {
1058						for who in whos.into_iter() {
1059							T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0);
1060						}
1061					}
1062					Self::clear_metadata(MetadataOwner::Proposal(prop_index));
1063				}
1064			});
1065
1066			// Remove the external queued referendum, if it's there.
1067			if matches!(NextExternal::<T>::get(), Some((p, ..)) if p.hash() == proposal_hash) {
1068				NextExternal::<T>::kill();
1069				Self::clear_metadata(MetadataOwner::External);
1070			}
1071
1072			// Remove the referendum, if it's there.
1073			if let Some(ref_index) = maybe_ref_index {
1074				if let Ok(status) = Self::referendum_status(ref_index) {
1075					if status.proposal.hash() == proposal_hash {
1076						Self::internal_cancel_referendum(ref_index);
1077					}
1078				}
1079			}
1080
1081			Self::deposit_event(Event::<T>::Blacklisted { proposal_hash });
1082			Ok(())
1083		}
1084
1085		/// Remove a proposal.
1086		///
1087		/// The dispatch origin of this call must be `CancelProposalOrigin`.
1088		///
1089		/// - `prop_index`: The index of the proposal to cancel.
1090		///
1091		/// Weight: `O(p)` where `p = PublicProps::<T>::decode_len()`
1092		#[pezpallet::call_index(17)]
1093		#[pezpallet::weight(T::WeightInfo::cancel_proposal())]
1094		pub fn cancel_proposal(
1095			origin: OriginFor<T>,
1096			#[pezpallet::compact] prop_index: PropIndex,
1097		) -> DispatchResult {
1098			T::CancelProposalOrigin::ensure_origin(origin)?;
1099
1100			PublicProps::<T>::mutate(|props| props.retain(|p| p.0 != prop_index));
1101			if let Some((whos, amount)) = DepositOf::<T>::take(prop_index) {
1102				for who in whos.into_iter() {
1103					T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0);
1104				}
1105			}
1106			Self::deposit_event(Event::<T>::ProposalCanceled { prop_index });
1107			Self::clear_metadata(MetadataOwner::Proposal(prop_index));
1108			Ok(())
1109		}
1110
1111		/// Set or clear a metadata of a proposal or a referendum.
1112		///
1113		/// Parameters:
1114		/// - `origin`: Must correspond to the `MetadataOwner`.
1115		///     - `ExternalOrigin` for an external proposal with the `SuperMajorityApprove`
1116		///       threshold.
1117		///     - `ExternalDefaultOrigin` for an external proposal with the `SuperMajorityAgainst`
1118		///       threshold.
1119		///     - `ExternalMajorityOrigin` for an external proposal with the `SimpleMajority`
1120		///       threshold.
1121		///     - `Signed` by a creator for a public proposal.
1122		///     - `Signed` to clear a metadata for a finished referendum.
1123		///     - `Root` to set a metadata for an ongoing referendum.
1124		/// - `owner`: an identifier of a metadata owner.
1125		/// - `maybe_hash`: The hash of an on-chain stored preimage. `None` to clear a metadata.
1126		#[pezpallet::call_index(18)]
1127		#[pezpallet::weight(
1128			match (owner, maybe_hash) {
1129				(MetadataOwner::External, Some(_)) => T::WeightInfo::set_external_metadata(),
1130				(MetadataOwner::External, None) => T::WeightInfo::clear_external_metadata(),
1131				(MetadataOwner::Proposal(_), Some(_)) => T::WeightInfo::set_proposal_metadata(),
1132				(MetadataOwner::Proposal(_), None) => T::WeightInfo::clear_proposal_metadata(),
1133				(MetadataOwner::Referendum(_), Some(_)) => T::WeightInfo::set_referendum_metadata(),
1134				(MetadataOwner::Referendum(_), None) => T::WeightInfo::clear_referendum_metadata(),
1135			}
1136		)]
1137		pub fn set_metadata(
1138			origin: OriginFor<T>,
1139			owner: MetadataOwner,
1140			maybe_hash: Option<T::Hash>,
1141		) -> DispatchResult {
1142			match owner {
1143				MetadataOwner::External => {
1144					let (_, threshold) = NextExternal::<T>::get().ok_or(Error::<T>::NoProposal)?;
1145					Self::ensure_external_origin(threshold, origin)?;
1146				},
1147				MetadataOwner::Proposal(index) => {
1148					let who = ensure_signed(origin)?;
1149					let (_, _, proposer) = Self::proposal(index)?;
1150					ensure!(proposer == who, Error::<T>::NoPermission);
1151				},
1152				MetadataOwner::Referendum(index) => {
1153					let is_root = ensure_signed_or_root(origin)?.is_none();
1154					ensure!(is_root || maybe_hash.is_none(), Error::<T>::NoPermission);
1155					ensure!(
1156						is_root || Self::referendum_status(index).is_err(),
1157						Error::<T>::NoPermission
1158					);
1159				},
1160			}
1161			if let Some(hash) = maybe_hash {
1162				ensure!(T::Preimages::len(&hash).is_some(), Error::<T>::PreimageNotExist);
1163				MetadataOf::<T>::insert(owner.clone(), hash);
1164				Self::deposit_event(Event::<T>::MetadataSet { owner, hash });
1165			} else {
1166				Self::clear_metadata(owner);
1167			}
1168			Ok(())
1169		}
1170	}
1171}
1172
1173pub trait EncodeInto: Encode {
1174	fn encode_into<T: AsMut<[u8]> + Default, H: pezsp_core::Hasher>(&self) -> T {
1175		let mut t = T::default();
1176		self.using_encoded(|data| {
1177			if data.len() <= t.as_mut().len() {
1178				t.as_mut()[0..data.len()].copy_from_slice(data);
1179			} else {
1180				// encoded self is too big to fit into a T.
1181				// hash it and use the first bytes of that instead.
1182				let hash = H::hash(&data);
1183				let hash = hash.as_ref();
1184				let l = t.as_mut().len().min(hash.len());
1185				t.as_mut()[0..l].copy_from_slice(&hash[0..l]);
1186			}
1187		});
1188		t
1189	}
1190}
1191impl<T: Encode> EncodeInto for T {}
1192
1193impl<T: Config> Pezpallet<T> {
1194	// exposed immutables.
1195
1196	/// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal
1197	/// index.
1198	pub fn backing_for(proposal: PropIndex) -> Option<BalanceOf<T>> {
1199		DepositOf::<T>::get(proposal).map(|(l, d)| d.saturating_mul((l.len() as u32).into()))
1200	}
1201
1202	/// Get all referenda ready for tally at block `n`.
1203	pub fn maturing_referenda_at(
1204		n: BlockNumberFor<T>,
1205	) -> Vec<(ReferendumIndex, ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>)>
1206	{
1207		let next = LowestUnbaked::<T>::get();
1208		let last = ReferendumCount::<T>::get();
1209		Self::maturing_referenda_at_inner(n, next..last)
1210	}
1211
1212	fn maturing_referenda_at_inner(
1213		n: BlockNumberFor<T>,
1214		range: core::ops::Range<PropIndex>,
1215	) -> Vec<(ReferendumIndex, ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>)>
1216	{
1217		range
1218			.into_iter()
1219			.map(|i| (i, ReferendumInfoOf::<T>::get(i)))
1220			.filter_map(|(i, maybe_info)| match maybe_info {
1221				Some(ReferendumInfo::Ongoing(status)) => Some((i, status)),
1222				_ => None,
1223			})
1224			.filter(|(_, status)| status.end == n)
1225			.collect()
1226	}
1227
1228	// Exposed mutables.
1229
1230	/// Start a referendum.
1231	pub fn internal_start_referendum(
1232		proposal: BoundedCallOf<T>,
1233		threshold: VoteThreshold,
1234		delay: BlockNumberFor<T>,
1235	) -> ReferendumIndex {
1236		Pezpallet::<T>::inject_referendum(
1237			pezframe_system::Pezpallet::<T>::block_number().saturating_add(T::VotingPeriod::get()),
1238			proposal,
1239			threshold,
1240			delay,
1241		)
1242	}
1243
1244	/// Remove a referendum.
1245	pub fn internal_cancel_referendum(ref_index: ReferendumIndex) {
1246		Self::deposit_event(Event::<T>::Cancelled { ref_index });
1247		ReferendumInfoOf::<T>::remove(ref_index);
1248		Self::clear_metadata(MetadataOwner::Referendum(ref_index));
1249	}
1250
1251	// private.
1252
1253	/// Ok if the given referendum is active, Err otherwise
1254	fn ensure_ongoing(
1255		r: ReferendumInfo<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>,
1256	) -> Result<ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>, DispatchError>
1257	{
1258		match r {
1259			ReferendumInfo::Ongoing(s) => Ok(s),
1260			_ => Err(Error::<T>::ReferendumInvalid.into()),
1261		}
1262	}
1263
1264	fn referendum_status(
1265		ref_index: ReferendumIndex,
1266	) -> Result<ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>, DispatchError>
1267	{
1268		let info = ReferendumInfoOf::<T>::get(ref_index).ok_or(Error::<T>::ReferendumInvalid)?;
1269		Self::ensure_ongoing(info)
1270	}
1271
1272	/// Actually enact a vote, if legit.
1273	fn try_vote(
1274		who: &T::AccountId,
1275		ref_index: ReferendumIndex,
1276		vote: AccountVote<BalanceOf<T>>,
1277	) -> DispatchResult {
1278		let mut status = Self::referendum_status(ref_index)?;
1279		ensure!(vote.balance() <= T::Currency::free_balance(who), Error::<T>::InsufficientFunds);
1280		VotingOf::<T>::try_mutate(who, |voting| -> DispatchResult {
1281			if let Voting::Direct { ref mut votes, delegations, .. } = voting {
1282				match votes.binary_search_by_key(&ref_index, |i| i.0) {
1283					Ok(i) => {
1284						// Shouldn't be possible to fail, but we handle it gracefully.
1285						status.tally.remove(votes[i].1).ok_or(ArithmeticError::Underflow)?;
1286						if let Some(approve) = votes[i].1.as_standard() {
1287							status.tally.reduce(approve, *delegations);
1288						}
1289						votes[i].1 = vote;
1290					},
1291					Err(i) => {
1292						votes
1293							.try_insert(i, (ref_index, vote))
1294							.map_err(|_| Error::<T>::MaxVotesReached)?;
1295					},
1296				}
1297				Self::deposit_event(Event::<T>::Voted { voter: who.clone(), ref_index, vote });
1298				// Shouldn't be possible to fail, but we handle it gracefully.
1299				status.tally.add(vote).ok_or(ArithmeticError::Overflow)?;
1300				if let Some(approve) = vote.as_standard() {
1301					status.tally.increase(approve, *delegations);
1302				}
1303				Ok(())
1304			} else {
1305				Err(Error::<T>::AlreadyDelegating.into())
1306			}
1307		})?;
1308		// Extend the lock to `balance` (rather than setting it) since we don't know what other
1309		// votes are in place.
1310		T::Currency::extend_lock(
1311			DEMOCRACY_ID,
1312			who,
1313			vote.balance(),
1314			WithdrawReasons::except(WithdrawReasons::RESERVE),
1315		);
1316		ReferendumInfoOf::<T>::insert(ref_index, ReferendumInfo::Ongoing(status));
1317		Ok(())
1318	}
1319
1320	/// Remove the account's vote for the given referendum if possible. This is possible when:
1321	/// - The referendum has not finished.
1322	/// - The referendum has finished and the voter lost their direction.
1323	/// - The referendum has finished and the voter's lock period is up.
1324	///
1325	/// This will generally be combined with a call to `unlock`.
1326	fn try_remove_vote(
1327		who: &T::AccountId,
1328		ref_index: ReferendumIndex,
1329		scope: UnvoteScope,
1330	) -> DispatchResult {
1331		let info = ReferendumInfoOf::<T>::get(ref_index);
1332		VotingOf::<T>::try_mutate(who, |voting| -> DispatchResult {
1333			if let Voting::Direct { ref mut votes, delegations, ref mut prior } = voting {
1334				let i = votes
1335					.binary_search_by_key(&ref_index, |i| i.0)
1336					.map_err(|_| Error::<T>::NotVoter)?;
1337				match info {
1338					Some(ReferendumInfo::Ongoing(mut status)) => {
1339						ensure!(matches!(scope, UnvoteScope::Any), Error::<T>::NoPermission);
1340						// Shouldn't be possible to fail, but we handle it gracefully.
1341						status.tally.remove(votes[i].1).ok_or(ArithmeticError::Underflow)?;
1342						if let Some(approve) = votes[i].1.as_standard() {
1343							status.tally.reduce(approve, *delegations);
1344						}
1345						ReferendumInfoOf::<T>::insert(ref_index, ReferendumInfo::Ongoing(status));
1346					},
1347					Some(ReferendumInfo::Finished { end, approved }) => {
1348						if let Some((lock_periods, balance)) = votes[i].1.locked_if(approved) {
1349							let unlock_at = end.saturating_add(
1350								T::VoteLockingPeriod::get().saturating_mul(lock_periods.into()),
1351							);
1352							let now = pezframe_system::Pezpallet::<T>::block_number();
1353							if now < unlock_at {
1354								ensure!(
1355									matches!(scope, UnvoteScope::Any),
1356									Error::<T>::NoPermission
1357								);
1358								prior.accumulate(unlock_at, balance)
1359							}
1360						}
1361					},
1362					None => {}, // Referendum was cancelled.
1363				}
1364				votes.remove(i);
1365			}
1366			Ok(())
1367		})?;
1368		Ok(())
1369	}
1370
1371	/// Return the number of votes for `who`
1372	fn increase_upstream_delegation(who: &T::AccountId, amount: Delegations<BalanceOf<T>>) -> u32 {
1373		VotingOf::<T>::mutate(who, |voting| match voting {
1374			Voting::Delegating { delegations, .. } => {
1375				// We don't support second level delegating, so we don't need to do anything more.
1376				*delegations = delegations.saturating_add(amount);
1377				1
1378			},
1379			Voting::Direct { votes, delegations, .. } => {
1380				*delegations = delegations.saturating_add(amount);
1381				for &(ref_index, account_vote) in votes.iter() {
1382					if let AccountVote::Standard { vote, .. } = account_vote {
1383						ReferendumInfoOf::<T>::mutate(ref_index, |maybe_info| {
1384							if let Some(ReferendumInfo::Ongoing(ref mut status)) = maybe_info {
1385								status.tally.increase(vote.aye, amount);
1386							}
1387						});
1388					}
1389				}
1390				votes.len() as u32
1391			},
1392		})
1393	}
1394
1395	/// Return the number of votes for `who`
1396	fn reduce_upstream_delegation(who: &T::AccountId, amount: Delegations<BalanceOf<T>>) -> u32 {
1397		VotingOf::<T>::mutate(who, |voting| match voting {
1398			Voting::Delegating { delegations, .. } => {
1399				// We don't support second level delegating, so we don't need to do anything more.
1400				*delegations = delegations.saturating_sub(amount);
1401				1
1402			},
1403			Voting::Direct { votes, delegations, .. } => {
1404				*delegations = delegations.saturating_sub(amount);
1405				for &(ref_index, account_vote) in votes.iter() {
1406					if let AccountVote::Standard { vote, .. } = account_vote {
1407						ReferendumInfoOf::<T>::mutate(ref_index, |maybe_info| {
1408							if let Some(ReferendumInfo::Ongoing(ref mut status)) = maybe_info {
1409								status.tally.reduce(vote.aye, amount);
1410							}
1411						});
1412					}
1413				}
1414				votes.len() as u32
1415			},
1416		})
1417	}
1418
1419	/// Attempt to delegate `balance` times `conviction` of voting power from `who` to `target`.
1420	///
1421	/// Return the upstream number of votes.
1422	fn try_delegate(
1423		who: T::AccountId,
1424		target: T::AccountId,
1425		conviction: Conviction,
1426		balance: BalanceOf<T>,
1427	) -> Result<u32, DispatchError> {
1428		ensure!(who != target, Error::<T>::Nonsense);
1429		ensure!(balance <= T::Currency::free_balance(&who), Error::<T>::InsufficientFunds);
1430		let votes = VotingOf::<T>::try_mutate(&who, |voting| -> Result<u32, DispatchError> {
1431			let mut old = Voting::Delegating {
1432				balance,
1433				target: target.clone(),
1434				conviction,
1435				delegations: Default::default(),
1436				prior: Default::default(),
1437			};
1438			core::mem::swap(&mut old, voting);
1439			match old {
1440				Voting::Delegating {
1441					balance, target, conviction, delegations, mut prior, ..
1442				} => {
1443					// remove any delegation votes to our current target.
1444					Self::reduce_upstream_delegation(&target, conviction.votes(balance));
1445					let now = pezframe_system::Pezpallet::<T>::block_number();
1446					let lock_periods = conviction.lock_periods().into();
1447					let unlock_block = now
1448						.saturating_add(T::VoteLockingPeriod::get().saturating_mul(lock_periods));
1449					prior.accumulate(unlock_block, balance);
1450					voting.set_common(delegations, prior);
1451				},
1452				Voting::Direct { votes, delegations, prior } => {
1453					// here we just ensure that we're currently idling with no votes recorded.
1454					ensure!(votes.is_empty(), Error::<T>::VotesExist);
1455					voting.set_common(delegations, prior);
1456				},
1457			}
1458			let votes = Self::increase_upstream_delegation(&target, conviction.votes(balance));
1459			// Extend the lock to `balance` (rather than setting it) since we don't know what other
1460			// votes are in place.
1461			T::Currency::extend_lock(
1462				DEMOCRACY_ID,
1463				&who,
1464				balance,
1465				WithdrawReasons::except(WithdrawReasons::RESERVE),
1466			);
1467			Ok(votes)
1468		})?;
1469		Self::deposit_event(Event::<T>::Delegated { who, target });
1470		Ok(votes)
1471	}
1472
1473	/// Attempt to end the current delegation.
1474	///
1475	/// Return the number of votes of upstream.
1476	fn try_undelegate(who: T::AccountId) -> Result<u32, DispatchError> {
1477		let votes = VotingOf::<T>::try_mutate(&who, |voting| -> Result<u32, DispatchError> {
1478			let mut old = Voting::default();
1479			core::mem::swap(&mut old, voting);
1480			match old {
1481				Voting::Delegating { balance, target, conviction, delegations, mut prior } => {
1482					// remove any delegation votes to our current target.
1483					let votes =
1484						Self::reduce_upstream_delegation(&target, conviction.votes(balance));
1485					let now = pezframe_system::Pezpallet::<T>::block_number();
1486					let lock_periods = conviction.lock_periods().into();
1487					let unlock_block = now
1488						.saturating_add(T::VoteLockingPeriod::get().saturating_mul(lock_periods));
1489					prior.accumulate(unlock_block, balance);
1490					voting.set_common(delegations, prior);
1491
1492					Ok(votes)
1493				},
1494				Voting::Direct { .. } => Err(Error::<T>::NotDelegating.into()),
1495			}
1496		})?;
1497		Self::deposit_event(Event::<T>::Undelegated { account: who });
1498		Ok(votes)
1499	}
1500
1501	/// Rejig the lock on an account. It will never get more stringent (since that would indicate
1502	/// a security hole) but may be reduced from what they are currently.
1503	fn update_lock(who: &T::AccountId) {
1504		let lock_needed = VotingOf::<T>::mutate(who, |voting| {
1505			voting.rejig(pezframe_system::Pezpallet::<T>::block_number());
1506			voting.locked_balance()
1507		});
1508		if lock_needed.is_zero() {
1509			T::Currency::remove_lock(DEMOCRACY_ID, who);
1510		} else {
1511			T::Currency::set_lock(
1512				DEMOCRACY_ID,
1513				who,
1514				lock_needed,
1515				WithdrawReasons::except(WithdrawReasons::RESERVE),
1516			);
1517		}
1518	}
1519
1520	/// Start a referendum
1521	fn inject_referendum(
1522		end: BlockNumberFor<T>,
1523		proposal: BoundedCallOf<T>,
1524		threshold: VoteThreshold,
1525		delay: BlockNumberFor<T>,
1526	) -> ReferendumIndex {
1527		let ref_index = ReferendumCount::<T>::get();
1528		ReferendumCount::<T>::put(ref_index + 1);
1529		let status =
1530			ReferendumStatus { end, proposal, threshold, delay, tally: Default::default() };
1531		let item = ReferendumInfo::Ongoing(status);
1532		ReferendumInfoOf::<T>::insert(ref_index, item);
1533		Self::deposit_event(Event::<T>::Started { ref_index, threshold });
1534		ref_index
1535	}
1536
1537	/// Table the next waiting proposal for a vote.
1538	fn launch_next(now: BlockNumberFor<T>) -> DispatchResult {
1539		if LastTabledWasExternal::<T>::take() {
1540			Self::launch_public(now).or_else(|_| Self::launch_external(now))
1541		} else {
1542			Self::launch_external(now).or_else(|_| Self::launch_public(now))
1543		}
1544		.map_err(|_| Error::<T>::NoneWaiting.into())
1545	}
1546
1547	/// Table the waiting external proposal for a vote, if there is one.
1548	fn launch_external(now: BlockNumberFor<T>) -> DispatchResult {
1549		if let Some((proposal, threshold)) = NextExternal::<T>::take() {
1550			LastTabledWasExternal::<T>::put(true);
1551			Self::deposit_event(Event::<T>::ExternalTabled);
1552			let ref_index = Self::inject_referendum(
1553				now.saturating_add(T::VotingPeriod::get()),
1554				proposal,
1555				threshold,
1556				T::EnactmentPeriod::get(),
1557			);
1558			Self::transfer_metadata(MetadataOwner::External, MetadataOwner::Referendum(ref_index));
1559			Ok(())
1560		} else {
1561			return Err(Error::<T>::NoneWaiting.into());
1562		}
1563	}
1564
1565	/// Table the waiting public proposal with the highest backing for a vote.
1566	fn launch_public(now: BlockNumberFor<T>) -> DispatchResult {
1567		let mut public_props = PublicProps::<T>::get();
1568		if let Some((winner_index, _)) = public_props.iter().enumerate().max_by_key(
1569			// defensive only: All current public proposals have an amount locked
1570			|x| Self::backing_for((x.1).0).defensive_unwrap_or_else(Zero::zero),
1571		) {
1572			let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
1573			PublicProps::<T>::put(public_props);
1574
1575			if let Some((depositors, deposit)) = DepositOf::<T>::take(prop_index) {
1576				// refund depositors
1577				for d in depositors.iter() {
1578					T::Currency::unreserve(d, deposit);
1579				}
1580				Self::deposit_event(Event::<T>::Tabled { proposal_index: prop_index, deposit });
1581				let ref_index = Self::inject_referendum(
1582					now.saturating_add(T::VotingPeriod::get()),
1583					proposal,
1584					VoteThreshold::SuperMajorityApprove,
1585					T::EnactmentPeriod::get(),
1586				);
1587				Self::transfer_metadata(
1588					MetadataOwner::Proposal(prop_index),
1589					MetadataOwner::Referendum(ref_index),
1590				)
1591			}
1592			Ok(())
1593		} else {
1594			return Err(Error::<T>::NoneWaiting.into());
1595		}
1596	}
1597
1598	fn bake_referendum(
1599		now: BlockNumberFor<T>,
1600		index: ReferendumIndex,
1601		status: ReferendumStatus<BlockNumberFor<T>, BoundedCallOf<T>, BalanceOf<T>>,
1602	) -> bool {
1603		let total_issuance = T::Currency::total_issuance();
1604		let approved = status.threshold.approved(status.tally, total_issuance);
1605
1606		if approved {
1607			Self::deposit_event(Event::<T>::Passed { ref_index: index });
1608
1609			// Earliest it can be scheduled for is next block.
1610			let when = now.saturating_add(status.delay.max(One::one()));
1611			if T::Scheduler::schedule_named(
1612				(DEMOCRACY_ID, index).encode_into::<_, T::Hashing>(),
1613				DispatchTime::At(when),
1614				None,
1615				63,
1616				pezframe_system::RawOrigin::Root.into(),
1617				status.proposal,
1618			)
1619			.is_err()
1620			{
1621				pezframe_support::print("LOGIC ERROR: bake_referendum/schedule_named failed");
1622			}
1623		} else {
1624			Self::deposit_event(Event::<T>::NotPassed { ref_index: index });
1625		}
1626
1627		approved
1628	}
1629
1630	/// Current era is ending; we should finish up any proposals.
1631	///
1632	///
1633	/// ## Complexity:
1634	/// If a referendum is launched or maturing, this will take full block weight if queue is not
1635	/// empty. Otherwise, `O(R)` where `R` is the number of unbaked referenda.
1636	fn begin_block(now: BlockNumberFor<T>) -> Weight {
1637		let max_block_weight = T::BlockWeights::get().max_block;
1638		let mut weight = Weight::zero();
1639
1640		let next = LowestUnbaked::<T>::get();
1641		let last = ReferendumCount::<T>::get();
1642		let r = last.saturating_sub(next);
1643
1644		// pick out another public referendum if it's time.
1645		if (now % T::LaunchPeriod::get()).is_zero() {
1646			// Errors come from the queue being empty. If the queue is not empty, it will take
1647			// full block weight.
1648			if Self::launch_next(now).is_ok() {
1649				weight = max_block_weight;
1650			} else {
1651				weight.saturating_accrue(T::WeightInfo::on_initialize_base_with_launch_period(r));
1652			}
1653		} else {
1654			weight.saturating_accrue(T::WeightInfo::on_initialize_base(r));
1655		}
1656
1657		// tally up votes for any expiring referenda.
1658		for (index, info) in Self::maturing_referenda_at_inner(now, next..last).into_iter() {
1659			let approved = Self::bake_referendum(now, index, info);
1660			ReferendumInfoOf::<T>::insert(index, ReferendumInfo::Finished { end: now, approved });
1661			weight = max_block_weight;
1662		}
1663
1664		// Notes:
1665		// * We don't consider the lowest unbaked to be the last maturing in case some referenda
1666		//   have a longer voting period than others.
1667		// * The iteration here shouldn't trigger any storage read that are not in cache, due to
1668		//   `maturing_referenda_at_inner` having already read them.
1669		// * We shouldn't iterate more than `LaunchPeriod/VotingPeriod + 1` times because the number
1670		//   of unbaked referendum is bounded by this number. In case those number have changed in a
1671		//   runtime upgrade the formula should be adjusted but the bound should still be sensible.
1672		LowestUnbaked::<T>::mutate(|ref_index| {
1673			while *ref_index < last
1674				&& ReferendumInfoOf::<T>::get(*ref_index)
1675					.map_or(true, |info| matches!(info, ReferendumInfo::Finished { .. }))
1676			{
1677				*ref_index += 1
1678			}
1679		});
1680
1681		weight
1682	}
1683
1684	/// Reads the length of account in DepositOf without getting the complete value in the runtime.
1685	///
1686	/// Return 0 if no deposit for this proposal.
1687	fn len_of_deposit_of(proposal: PropIndex) -> Option<u32> {
1688		// DepositOf first tuple element is a vec, decoding its len is equivalent to decode a
1689		// `Compact<u32>`.
1690		decode_compact_u32_at(&DepositOf::<T>::hashed_key_for(proposal))
1691	}
1692
1693	/// Return a proposal of an index.
1694	fn proposal(index: PropIndex) -> Result<(PropIndex, BoundedCallOf<T>, T::AccountId), Error<T>> {
1695		PublicProps::<T>::get()
1696			.into_iter()
1697			.find(|(prop_index, _, _)| prop_index == &index)
1698			.ok_or(Error::<T>::ProposalMissing)
1699	}
1700
1701	/// Clear metadata if exist for a given owner.
1702	fn clear_metadata(owner: MetadataOwner) {
1703		if let Some(hash) = MetadataOf::<T>::take(&owner) {
1704			Self::deposit_event(Event::<T>::MetadataCleared { owner, hash });
1705		}
1706	}
1707
1708	/// Transfer the metadata of an `owner` to a `new_owner`.
1709	fn transfer_metadata(owner: MetadataOwner, new_owner: MetadataOwner) {
1710		if let Some(hash) = MetadataOf::<T>::take(&owner) {
1711			MetadataOf::<T>::insert(&new_owner, hash);
1712			Self::deposit_event(Event::<T>::MetadataTransferred {
1713				prev_owner: owner,
1714				owner: new_owner,
1715				hash,
1716			});
1717		}
1718	}
1719
1720	/// Ensure external origin for corresponding vote threshold.
1721	fn ensure_external_origin(
1722		threshold: VoteThreshold,
1723		origin: OriginFor<T>,
1724	) -> Result<(), BadOrigin> {
1725		match threshold {
1726			VoteThreshold::SuperMajorityApprove => {
1727				T::ExternalOrigin::ensure_origin(origin)?;
1728			},
1729			VoteThreshold::SuperMajorityAgainst => {
1730				T::ExternalDefaultOrigin::ensure_origin(origin)?;
1731			},
1732			VoteThreshold::SimpleMajority => {
1733				T::ExternalMajorityOrigin::ensure_origin(origin)?;
1734			},
1735		};
1736		Ok(())
1737	}
1738}
1739
1740/// Decode `Compact<u32>` from the trie at given key.
1741fn decode_compact_u32_at(key: &[u8]) -> Option<u32> {
1742	// `Compact<u32>` takes at most 5 bytes.
1743	let mut buf = [0u8; 5];
1744	let bytes = pezsp_io::storage::read(key, &mut buf, 0)?;
1745	// The value may be smaller than 5 bytes.
1746	let mut input = &buf[0..buf.len().min(bytes as usize)];
1747	match codec::Compact::<u32>::decode(&mut input) {
1748		Ok(c) => Some(c.0),
1749		Err(_) => {
1750			pezsp_runtime::print("Failed to decode compact u32 at:");
1751			pezsp_runtime::print(key);
1752			None
1753		},
1754	}
1755}