pallet_collective_content/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
34
35#[cfg(test)]
36mod mock;
37#[cfg(test)]
38mod tests;
39
40#[cfg(feature = "runtime-benchmarks")]
41mod benchmarking;
42pub mod weights;
43
44pub use pallet::*;
45pub use weights::WeightInfo;
46
47use frame_support::{traits::schedule::DispatchTime, BoundedVec};
48use sp_core::ConstU32;
49
50pub type OpaqueCid = BoundedVec<u8, ConstU32<68>>;
53
54#[frame_support::pallet]
55pub mod pallet {
56 use super::*;
57 use frame_support::{ensure, pallet_prelude::*};
58 use frame_system::pallet_prelude::*;
59 use sp_runtime::{traits::BadOrigin, Saturating};
60
61 const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
63
64 #[pallet::pallet]
65 #[pallet::storage_version(STORAGE_VERSION)]
66 pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
67
68 #[pallet::config]
70 pub trait Config<I: 'static = ()>: frame_system::Config {
71 #[allow(deprecated)]
73 type RuntimeEvent: From<Event<Self, I>>
74 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
75
76 type AnnouncementLifetime: Get<BlockNumberFor<Self>>;
78
79 type AnnouncementOrigin: EnsureOrigin<Self::RuntimeOrigin>;
81
82 #[pallet::constant]
84 type MaxAnnouncements: Get<u32>;
85
86 type CharterOrigin: EnsureOrigin<Self::RuntimeOrigin>;
88
89 type WeightInfo: WeightInfo;
91 }
92
93 #[pallet::error]
94 pub enum Error<T, I = ()> {
95 MissingAnnouncement,
97 TooManyAnnouncements,
99 InvalidExpiration,
101 }
102
103 #[pallet::event]
104 #[pallet::generate_deposit(pub(super) fn deposit_event)]
105 pub enum Event<T: Config<I>, I: 'static = ()> {
106 NewCharterSet { cid: OpaqueCid },
108 AnnouncementAnnounced { cid: OpaqueCid, expire_at: BlockNumberFor<T> },
110 AnnouncementRemoved { cid: OpaqueCid },
112 }
113
114 #[pallet::storage]
116 pub type Charter<T: Config<I>, I: 'static = ()> = StorageValue<_, OpaqueCid, OptionQuery>;
117
118 #[pallet::storage]
120 pub type Announcements<T: Config<I>, I: 'static = ()> =
121 CountedStorageMap<_, Blake2_128Concat, OpaqueCid, BlockNumberFor<T>, OptionQuery>;
122
123 #[pallet::call]
124 impl<T: Config<I>, I: 'static> Pallet<T, I> {
125 #[pallet::call_index(0)]
131 #[pallet::weight(T::WeightInfo::set_charter())]
132 pub fn set_charter(origin: OriginFor<T>, cid: OpaqueCid) -> DispatchResult {
133 T::CharterOrigin::ensure_origin(origin)?;
134
135 Charter::<T, I>::put(&cid);
136
137 Self::deposit_event(Event::<T, I>::NewCharterSet { cid });
138 Ok(())
139 }
140
141 #[pallet::call_index(1)]
150 #[pallet::weight(T::WeightInfo::announce())]
151 pub fn announce(
152 origin: OriginFor<T>,
153 cid: OpaqueCid,
154 maybe_expire: Option<DispatchTime<BlockNumberFor<T>>>,
155 ) -> DispatchResult {
156 T::AnnouncementOrigin::ensure_origin(origin)?;
157
158 let now = frame_system::Pallet::<T>::block_number();
159 let expire_at = maybe_expire
160 .map_or(now.saturating_add(T::AnnouncementLifetime::get()), |e| e.evaluate(now));
161 ensure!(expire_at > now, Error::<T, I>::InvalidExpiration);
162 ensure!(
163 T::MaxAnnouncements::get() > <Announcements<T, I>>::count(),
164 Error::<T, I>::TooManyAnnouncements
165 );
166
167 <Announcements<T, I>>::insert(cid.clone(), expire_at);
168
169 Self::deposit_event(Event::<T, I>::AnnouncementAnnounced { cid, expire_at });
170 Ok(())
171 }
172
173 #[pallet::call_index(2)]
182 #[pallet::weight(T::WeightInfo::remove_announcement())]
183 pub fn remove_announcement(
184 origin: OriginFor<T>,
185 cid: OpaqueCid,
186 ) -> DispatchResultWithPostInfo {
187 let maybe_who = match T::AnnouncementOrigin::try_origin(origin) {
188 Ok(_) => None,
189 Err(origin) => Some(ensure_signed(origin)?),
190 };
191 let expire_at = <Announcements<T, I>>::get(cid.clone())
192 .ok_or(Error::<T, I>::MissingAnnouncement)?;
193 let now = frame_system::Pallet::<T>::block_number();
194 ensure!(maybe_who.is_none() || now >= expire_at, BadOrigin);
195
196 <Announcements<T, I>>::remove(cid.clone());
197
198 Self::deposit_event(Event::<T, I>::AnnouncementRemoved { cid });
199
200 if now >= expire_at {
201 return Ok(Pays::No.into())
202 }
203 Ok(Pays::Yes.into())
204 }
205 }
206}