1#![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 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 type Scheduler: ScheduleNamed<
231 BlockNumberFor<Self>,
232 CallOf<Self>,
233 Self::PalletsOrigin,
234 Hasher = Self::Hashing,
235 >;
236
237 type Preimages: QueryPreimage<H = Self::Hashing> + StorePreimage;
239
240 type Currency: ReservableCurrency<Self::AccountId>
242 + LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
243
244 #[pezpallet::constant]
250 type EnactmentPeriod: Get<BlockNumberFor<Self>>;
251
252 #[pezpallet::constant]
254 type LaunchPeriod: Get<BlockNumberFor<Self>>;
255
256 #[pezpallet::constant]
258 type VotingPeriod: Get<BlockNumberFor<Self>>;
259
260 #[pezpallet::constant]
265 type VoteLockingPeriod: Get<BlockNumberFor<Self>>;
266
267 #[pezpallet::constant]
269 type MinimumDeposit: Get<BalanceOf<Self>>;
270
271 #[pezpallet::constant]
275 type InstantAllowed: Get<bool>;
276
277 #[pezpallet::constant]
279 type FastTrackVotingPeriod: Get<BlockNumberFor<Self>>;
280
281 #[pezpallet::constant]
283 type CooloffPeriod: Get<BlockNumberFor<Self>>;
284
285 #[pezpallet::constant]
290 type MaxVotes: Get<u32>;
291
292 #[pezpallet::constant]
294 type MaxProposals: Get<u32>;
295
296 #[pezpallet::constant]
298 type MaxDeposits: Get<u32>;
299
300 #[pezpallet::constant]
302 type MaxBlacklisted: Get<u32>;
303
304 type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
307
308 type ExternalMajorityOrigin: EnsureOrigin<Self::RuntimeOrigin>;
311
312 type ExternalDefaultOrigin: EnsureOrigin<Self::RuntimeOrigin>;
315
316 type SubmitOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
320
321 type FastTrackOrigin: EnsureOrigin<Self::RuntimeOrigin>;
325
326 type InstantOrigin: EnsureOrigin<Self::RuntimeOrigin>;
330
331 type CancellationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
333
334 type BlacklistOrigin: EnsureOrigin<Self::RuntimeOrigin>;
336
337 type CancelProposalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
339
340 type VetoOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
342
343 type PalletsOrigin: From<pezframe_system::RawOrigin<Self::AccountId>>;
345
346 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
348 }
349
350 #[pezpallet::storage]
352 pub type PublicPropCount<T> = StorageValue<_, PropIndex, ValueQuery>;
353
354 #[pezpallet::storage]
356 pub type PublicProps<T: Config> = StorageValue<
357 _,
358 BoundedVec<(PropIndex, BoundedCallOf<T>, T::AccountId), T::MaxProposals>,
359 ValueQuery,
360 >;
361
362 #[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 #[pezpallet::storage]
375 pub type ReferendumCount<T> = StorageValue<_, ReferendumIndex, ValueQuery>;
376
377 #[pezpallet::storage]
380 pub type LowestUnbaked<T> = StorageValue<_, ReferendumIndex, ValueQuery>;
381
382 #[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 #[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 #[pezpallet::storage]
409 pub type LastTabledWasExternal<T> = StorageValue<_, bool, ValueQuery>;
410
411 #[pezpallet::storage]
416 pub type NextExternal<T: Config> = StorageValue<_, (BoundedCallOf<T>, VoteThreshold)>;
417
418 #[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 #[pezpallet::storage]
430 pub type Cancellations<T: Config> = StorageMap<_, Identity, T::Hash, bool, ValueQuery>;
431
432 #[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 Proposed { proposal_index: PropIndex, deposit: BalanceOf<T> },
462 Tabled { proposal_index: PropIndex, deposit: BalanceOf<T> },
464 ExternalTabled,
466 Started { ref_index: ReferendumIndex, threshold: VoteThreshold },
468 Passed { ref_index: ReferendumIndex },
470 NotPassed { ref_index: ReferendumIndex },
472 Cancelled { ref_index: ReferendumIndex },
474 Delegated { who: T::AccountId, target: T::AccountId },
476 Undelegated { account: T::AccountId },
478 Vetoed { who: T::AccountId, proposal_hash: T::Hash, until: BlockNumberFor<T> },
480 Blacklisted { proposal_hash: T::Hash },
482 Voted { voter: T::AccountId, ref_index: ReferendumIndex, vote: AccountVote<BalanceOf<T>> },
484 Seconded { seconder: T::AccountId, prop_index: PropIndex },
486 ProposalCanceled { prop_index: PropIndex },
488 MetadataSet {
490 owner: MetadataOwner,
492 hash: T::Hash,
494 },
495 MetadataCleared {
497 owner: MetadataOwner,
499 hash: T::Hash,
501 },
502 MetadataTransferred {
504 prev_owner: MetadataOwner,
506 owner: MetadataOwner,
508 hash: T::Hash,
510 },
511 }
512
513 #[pezpallet::error]
514 pub enum Error<T> {
515 ValueLow,
517 ProposalMissing,
519 AlreadyCanceled,
521 DuplicateProposal,
523 ProposalBlacklisted,
525 NotSimpleMajority,
527 InvalidHash,
529 NoProposal,
531 AlreadyVetoed,
533 ReferendumInvalid,
535 NoneWaiting,
537 NotVoter,
539 NoPermission,
541 AlreadyDelegating,
543 InsufficientFunds,
545 NotDelegating,
547 VotesExist,
550 InstantNotAllowed,
552 Nonsense,
554 WrongUpperBound,
556 MaxVotesReached,
558 TooMany,
560 VotingPeriodLow,
562 PreimageNotExist,
564 }
565
566 #[pezpallet::hooks]
567 impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
568 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 let permanent =
1050 (BlockNumberFor::<T>::max_value(), BoundedVec::<T::AccountId, _>::default());
1051 Blacklist::<T>::insert(&proposal_hash, permanent);
1052
1053 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 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 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 #[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 #[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 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 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 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 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 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 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 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 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 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 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 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 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 => {}, }
1364 votes.remove(i);
1365 }
1366 Ok(())
1367 })?;
1368 Ok(())
1369 }
1370
1371 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 *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 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 *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 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 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 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 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 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 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 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 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 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 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 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 |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 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 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 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 if (now % T::LaunchPeriod::get()).is_zero() {
1646 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 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 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 fn len_of_deposit_of(proposal: PropIndex) -> Option<u32> {
1688 decode_compact_u32_at(&DepositOf::<T>::hashed_key_for(proposal))
1691 }
1692
1693 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 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 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 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
1740fn decode_compact_u32_at(key: &[u8]) -> Option<u32> {
1742 let mut buf = [0u8; 5];
1744 let bytes = pezsp_io::storage::read(key, &mut buf, 0)?;
1745 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}