Skip to main content

pallet_encointer_bazaar/
lib.rs

1// Copyright (c) 2019 Alain Brenzikofer
2// This file is part of Encointer
3//
4// Encointer is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Encointer is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Encointer.  If not, see <http://www.gnu.org/licenses/>.
16
17//! # Encointer Bazaar Module
18//!
19//! provides functionality for
20//! - creating new bazaar entries (shop and articles)
21//! - removing existing entries (shop and articles)
22
23#![cfg_attr(not(feature = "std"), no_std)]
24
25use frame_support::ensure;
26use frame_system::ensure_signed;
27use sp_std::prelude::*;
28
29use encointer_primitives::{
30	bazaar::{BusinessData, BusinessIdentifier, OfferingData, OfferingIdentifier},
31	common::PalletString,
32	communities::CommunityIdentifier,
33};
34
35pub use pallet::*;
36pub use weights::WeightInfo;
37
38#[frame_support::pallet]
39pub mod pallet {
40	use super::*;
41	use frame_support::pallet_prelude::*;
42	use frame_system::pallet_prelude::*;
43
44	#[pallet::config]
45	pub trait Config: frame_system::Config + pallet_encointer_communities::Config {
46		#[allow(deprecated)]
47		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
48		type WeightInfo: WeightInfo;
49	}
50
51	#[pallet::pallet]
52	#[pallet::without_storage_info]
53	pub struct Pallet<T>(PhantomData<T>);
54
55	#[pallet::call]
56	impl<T: Config> Pallet<T> {
57		#[pallet::call_index(0)]
58		#[pallet::weight((<T as Config>::WeightInfo::create_business(), DispatchClass::Normal, Pays::Yes))]
59		pub fn create_business(
60			origin: OriginFor<T>,
61			cid: CommunityIdentifier,
62			url: PalletString,
63		) -> DispatchResultWithPostInfo {
64			// Check that the extrinsic was signed and get the signer
65			let sender = ensure_signed(origin)?;
66			// Check that the supplied community is actually registered
67			ensure!(
68				<pallet_encointer_communities::Pallet<T>>::community_identifiers().contains(&cid),
69				Error::<T>::NonexistentCommunity
70			);
71
72			ensure!(
73				!BusinessRegistry::<T>::contains_key(cid, &sender),
74				Error::<T>::ExistingBusiness
75			);
76
77			BusinessRegistry::<T>::insert(cid, &sender, BusinessData::new(url, 1));
78
79			Self::deposit_event(Event::BusinessCreated(cid, sender));
80
81			Ok(().into())
82		}
83
84		#[pallet::call_index(1)]
85		#[pallet::weight((<T as Config>::WeightInfo::update_business(), DispatchClass::Normal, Pays::Yes))]
86		pub fn update_business(
87			origin: OriginFor<T>,
88			cid: CommunityIdentifier,
89			url: PalletString,
90		) -> DispatchResultWithPostInfo {
91			// Check that the extrinsic was signed and get the signer
92			let sender = ensure_signed(origin)?;
93
94			ensure!(
95				BusinessRegistry::<T>::contains_key(cid, &sender),
96				Error::<T>::NonexistentBusiness
97			);
98
99			BusinessRegistry::<T>::mutate(cid, &sender, |b| b.url = url);
100
101			Self::deposit_event(Event::BusinessUpdated(cid, sender));
102
103			Ok(().into())
104		}
105
106		#[pallet::call_index(2)]
107		#[pallet::weight((<T as Config>::WeightInfo::delete_business(), DispatchClass::Normal, Pays::Yes))]
108		pub fn delete_business(
109			origin: OriginFor<T>,
110			cid: CommunityIdentifier,
111		) -> DispatchResultWithPostInfo {
112			// Check that the extrinsic was signed and get the signer
113			let sender = ensure_signed(origin)?;
114
115			ensure!(
116				BusinessRegistry::<T>::contains_key(cid, &sender),
117				Error::<T>::NonexistentBusiness
118			);
119
120			BusinessRegistry::<T>::remove(cid, &sender);
121			#[allow(deprecated)]
122			OfferingRegistry::<T>::remove_prefix(
123				BusinessIdentifier::new(cid, sender.clone()),
124				None,
125			);
126
127			Self::deposit_event(Event::BusinessDeleted(cid, sender));
128
129			Ok(().into())
130		}
131
132		#[pallet::call_index(3)]
133		#[pallet::weight((<T as Config>::WeightInfo::create_offering(), DispatchClass::Normal, Pays::Yes))]
134		pub fn create_offering(
135			origin: OriginFor<T>,
136			cid: CommunityIdentifier,
137			url: PalletString,
138		) -> DispatchResultWithPostInfo {
139			// Check that the extrinsic was signed and get the signer
140			let sender = ensure_signed(origin)?;
141
142			ensure!(
143				BusinessRegistry::<T>::contains_key(cid, &sender),
144				Error::<T>::NonexistentBusiness
145			);
146
147			let oid = BusinessRegistry::<T>::get(cid, &sender).last_oid;
148			BusinessRegistry::<T>::mutate(cid, &sender, |b| b.last_oid += 1);
149			OfferingRegistry::<T>::insert(
150				BusinessIdentifier::new(cid, sender.clone()),
151				oid,
152				OfferingData::new(url),
153			);
154
155			Self::deposit_event(Event::OfferingCreated(cid, sender, oid));
156
157			Ok(().into())
158		}
159
160		#[pallet::call_index(4)]
161		#[pallet::weight((<T as Config>::WeightInfo::update_offering(), DispatchClass::Normal, Pays::Yes))]
162		pub fn update_offering(
163			origin: OriginFor<T>,
164			cid: CommunityIdentifier,
165			oid: OfferingIdentifier,
166			url: PalletString,
167		) -> DispatchResultWithPostInfo {
168			// Check that the extrinsic was signed and get the signer
169			let sender = ensure_signed(origin)?;
170
171			let business_identifier = BusinessIdentifier::new(cid, sender.clone());
172
173			ensure!(
174				OfferingRegistry::<T>::contains_key(&business_identifier, oid),
175				Error::<T>::NonexistentOffering
176			);
177
178			OfferingRegistry::<T>::mutate(business_identifier, oid, |o| o.url = url);
179
180			Self::deposit_event(Event::OfferingUpdated(cid, sender, oid));
181
182			Ok(().into())
183		}
184
185		#[pallet::call_index(5)]
186		#[pallet::weight((<T as Config>::WeightInfo::delete_offering(), DispatchClass::Normal, Pays::Yes))]
187		pub fn delete_offering(
188			origin: OriginFor<T>,
189			cid: CommunityIdentifier,
190			oid: OfferingIdentifier,
191		) -> DispatchResultWithPostInfo {
192			// Check that the extrinsic was signed and get the signer
193			let sender = ensure_signed(origin)?;
194
195			let business_identifier = BusinessIdentifier::new(cid, sender.clone());
196
197			ensure!(
198				OfferingRegistry::<T>::contains_key(&business_identifier, oid),
199				Error::<T>::NonexistentOffering
200			);
201
202			OfferingRegistry::<T>::remove(business_identifier, oid);
203
204			Self::deposit_event(Event::OfferingDeleted(cid, sender, oid));
205
206			Ok(().into())
207		}
208	}
209
210	#[pallet::event]
211	#[pallet::generate_deposit(pub(super) fn deposit_event)]
212	pub enum Event<T: Config> {
213		/// Event emitted when a business is created. [community, who]
214		BusinessCreated(CommunityIdentifier, T::AccountId),
215		/// Event emitted when a business is updated. [community, who]
216		BusinessUpdated(CommunityIdentifier, T::AccountId),
217		/// Event emitted when a business is deleted. [community, who]
218		BusinessDeleted(CommunityIdentifier, T::AccountId),
219		/// Event emitted when an offering is created. [community, who, oid]
220		OfferingCreated(CommunityIdentifier, T::AccountId, OfferingIdentifier),
221		/// Event emitted when an offering is updated. [community, who, oid]
222		OfferingUpdated(CommunityIdentifier, T::AccountId, OfferingIdentifier),
223		/// Event emitted when an offering is deleted. [community, who, oid]
224		OfferingDeleted(CommunityIdentifier, T::AccountId, OfferingIdentifier),
225	}
226
227	#[pallet::error]
228	pub enum Error<T> {
229		/// community identifier not found
230		NonexistentCommunity,
231		/// business already registered for this cid
232		ExistingBusiness,
233		/// business does not exist
234		NonexistentBusiness,
235		/// offering does not exist
236		NonexistentOffering,
237	}
238
239	#[pallet::storage]
240	#[pallet::getter(fn business_registry)]
241	pub type BusinessRegistry<T: Config> = StorageDoubleMap<
242		_,
243		Blake2_128Concat,
244		CommunityIdentifier,
245		Blake2_128Concat,
246		T::AccountId,
247		BusinessData,
248		ValueQuery,
249	>;
250
251	#[pallet::storage]
252	#[pallet::getter(fn offering_registry)]
253	pub type OfferingRegistry<T: Config> = StorageDoubleMap<
254		_,
255		Blake2_128Concat,
256		BusinessIdentifier<T::AccountId>,
257		Blake2_128Concat,
258		OfferingIdentifier,
259		OfferingData,
260		ValueQuery,
261	>;
262}
263
264impl<T: Config> Pallet<T> {
265	pub fn get_businesses(cid: &CommunityIdentifier) -> Vec<(T::AccountId, BusinessData)> {
266		BusinessRegistry::<T>::iter_prefix(cid).collect()
267	}
268
269	pub fn get_offerings(bid: &BusinessIdentifier<T::AccountId>) -> Vec<OfferingData> {
270		OfferingRegistry::<T>::iter_prefix_values(bid).collect()
271	}
272}
273
274mod weights;
275
276#[cfg(feature = "runtime-benchmarks")]
277mod benchmarking;
278#[cfg(test)]
279mod mock;
280#[cfg(test)]
281mod tests;