1use super::*;
21use frame_support::{
22 ensure,
23 traits::{ExistenceRequirement, Get},
24};
25use sp_runtime::{DispatchError, DispatchResult};
26
27impl<T: Config<I>, I: 'static> Pallet<T, I> {
28 pub fn do_transfer(
38 collection: T::CollectionId,
39 item: T::ItemId,
40 dest: T::AccountId,
41 with_details: impl FnOnce(
42 &CollectionDetailsFor<T, I>,
43 &mut ItemDetailsFor<T, I>,
44 ) -> DispatchResult,
45 ) -> DispatchResult {
46 let collection_details =
47 Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
48 ensure!(!collection_details.is_frozen, Error::<T, I>::Frozen);
49 ensure!(!T::Locker::is_locked(collection.clone(), item), Error::<T, I>::Locked);
50
51 let mut details =
52 Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownCollection)?;
53 ensure!(!details.is_frozen, Error::<T, I>::Frozen);
54 with_details(&collection_details, &mut details)?;
55
56 Account::<T, I>::remove((&details.owner, &collection, &item));
57 Account::<T, I>::insert((&dest, &collection, &item), ());
58 let origin = details.owner;
59 details.owner = dest;
60
61 details.approved = None;
65
66 Item::<T, I>::insert(&collection, &item, &details);
67 ItemPriceOf::<T, I>::remove(&collection, &item);
68
69 Self::deposit_event(Event::Transferred {
70 collection,
71 item,
72 from: origin,
73 to: details.owner,
74 });
75 Ok(())
76 }
77
78 pub fn do_create_collection(
85 collection: T::CollectionId,
86 owner: T::AccountId,
87 admin: T::AccountId,
88 deposit: DepositBalanceOf<T, I>,
89 free_holding: bool,
90 event: Event<T, I>,
91 ) -> DispatchResult {
92 ensure!(!Collection::<T, I>::contains_key(collection.clone()), Error::<T, I>::InUse);
93
94 T::Currency::reserve(&owner, deposit)?;
95
96 Collection::<T, I>::insert(
97 collection.clone(),
98 CollectionDetails {
99 owner: owner.clone(),
100 issuer: admin.clone(),
101 admin: admin.clone(),
102 freezer: admin,
103 total_deposit: deposit,
104 free_holding,
105 items: 0,
106 item_metadatas: 0,
107 attributes: 0,
108 is_frozen: false,
109 },
110 );
111
112 CollectionAccount::<T, I>::insert(&owner, &collection, ());
113 Self::deposit_event(event);
114 Ok(())
115 }
116
117 pub fn do_destroy_collection(
127 collection: T::CollectionId,
128 witness: DestroyWitness,
129 maybe_check_owner: Option<T::AccountId>,
130 ) -> Result<DestroyWitness, DispatchError> {
131 Collection::<T, I>::try_mutate_exists(collection.clone(), |maybe_details| {
132 let collection_details =
133 maybe_details.take().ok_or(Error::<T, I>::UnknownCollection)?;
134 if let Some(check_owner) = maybe_check_owner {
135 ensure!(collection_details.owner == check_owner, Error::<T, I>::NoPermission);
136 }
137 ensure!(collection_details.items == witness.items, Error::<T, I>::BadWitness);
138 ensure!(
139 collection_details.item_metadatas == witness.item_metadatas,
140 Error::<T, I>::BadWitness
141 );
142 ensure!(collection_details.attributes == witness.attributes, Error::<T, I>::BadWitness);
143
144 for (item, details) in Item::<T, I>::drain_prefix(&collection) {
145 Account::<T, I>::remove((&details.owner, &collection, &item));
146 }
147 #[allow(deprecated)]
148 ItemMetadataOf::<T, I>::remove_prefix(&collection, None);
149 #[allow(deprecated)]
150 ItemPriceOf::<T, I>::remove_prefix(&collection, None);
151 CollectionMetadataOf::<T, I>::remove(&collection);
152 #[allow(deprecated)]
153 Attribute::<T, I>::remove_prefix((&collection,), None);
154 CollectionAccount::<T, I>::remove(&collection_details.owner, &collection);
155 T::Currency::unreserve(&collection_details.owner, collection_details.total_deposit);
156 CollectionMaxSupply::<T, I>::remove(&collection);
157
158 Self::deposit_event(Event::Destroyed { collection });
159
160 Ok(DestroyWitness {
161 items: collection_details.items,
162 item_metadatas: collection_details.item_metadatas,
163 attributes: collection_details.attributes,
164 })
165 })
166 }
167
168 pub fn do_mint(
181 collection: T::CollectionId,
182 item: T::ItemId,
183 owner: T::AccountId,
184 with_details: impl FnOnce(&CollectionDetailsFor<T, I>) -> DispatchResult,
185 ) -> DispatchResult {
186 ensure!(
187 !Item::<T, I>::contains_key(collection.clone(), item),
188 Error::<T, I>::AlreadyExists
189 );
190
191 Collection::<T, I>::try_mutate(
192 &collection,
193 |maybe_collection_details| -> DispatchResult {
194 let collection_details =
195 maybe_collection_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
196
197 with_details(collection_details)?;
198
199 if let Ok(max_supply) = CollectionMaxSupply::<T, I>::try_get(&collection) {
200 ensure!(collection_details.items < max_supply, Error::<T, I>::MaxSupplyReached);
201 }
202
203 let items =
204 collection_details.items.checked_add(1).ok_or(ArithmeticError::Overflow)?;
205 collection_details.items = items;
206
207 let deposit = match collection_details.free_holding {
208 true => Zero::zero(),
209 false => T::ItemDeposit::get(),
210 };
211 T::Currency::reserve(&collection_details.owner, deposit)?;
212 collection_details.total_deposit += deposit;
213
214 let owner = owner.clone();
215 Account::<T, I>::insert((&owner, &collection, &item), ());
216 let details = ItemDetails { owner, approved: None, is_frozen: false, deposit };
217 Item::<T, I>::insert(&collection, &item, details);
218 Ok(())
219 },
220 )?;
221
222 Self::deposit_event(Event::Issued { collection, item, owner });
223 Ok(())
224 }
225
226 pub fn do_burn(
235 collection: T::CollectionId,
236 item: T::ItemId,
237 with_details: impl FnOnce(&CollectionDetailsFor<T, I>, &ItemDetailsFor<T, I>) -> DispatchResult,
238 ) -> DispatchResult {
239 ensure!(!T::Locker::is_locked(collection.clone(), item), Error::<T, I>::Locked);
240 let owner = Collection::<T, I>::try_mutate(
241 &collection,
242 |maybe_collection_details| -> Result<T::AccountId, DispatchError> {
243 let collection_details =
244 maybe_collection_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
245
246 let details = Item::<T, I>::get(&collection, &item)
248 .ok_or(Error::<T, I>::UnknownCollection)?;
249 with_details(collection_details, &details)?;
250
251 T::Currency::unreserve(&collection_details.owner, details.deposit);
253 collection_details.total_deposit.saturating_reduce(details.deposit);
254 collection_details.items.saturating_dec();
255 Ok(details.owner)
256 },
257 )?;
258
259 Item::<T, I>::remove(&collection, &item);
260 Account::<T, I>::remove((&owner, &collection, &item));
261 ItemPriceOf::<T, I>::remove(&collection, &item);
262
263 Self::deposit_event(Event::Burned { collection, item, owner });
264 Ok(())
265 }
266
267 pub fn do_set_price(
275 collection: T::CollectionId,
276 item: T::ItemId,
277 sender: T::AccountId,
278 price: Option<ItemPrice<T, I>>,
279 whitelisted_buyer: Option<T::AccountId>,
280 ) -> DispatchResult {
281 let details = Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
282 ensure!(details.owner == sender, Error::<T, I>::NoPermission);
283
284 if let Some(ref price) = price {
285 ItemPriceOf::<T, I>::insert(&collection, &item, (price, whitelisted_buyer.clone()));
286 Self::deposit_event(Event::ItemPriceSet {
287 collection,
288 item,
289 price: *price,
290 whitelisted_buyer,
291 });
292 } else {
293 ItemPriceOf::<T, I>::remove(&collection, &item);
294 Self::deposit_event(Event::ItemPriceRemoved { collection, item });
295 }
296
297 Ok(())
298 }
299
300 pub fn do_buy_item(
314 collection: T::CollectionId,
315 item: T::ItemId,
316 buyer: T::AccountId,
317 bid_price: ItemPrice<T, I>,
318 ) -> DispatchResult {
319 let details = Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
320 ensure!(details.owner != buyer, Error::<T, I>::NoPermission);
321
322 let price_info =
323 ItemPriceOf::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::NotForSale)?;
324
325 ensure!(bid_price >= price_info.0, Error::<T, I>::BidTooLow);
326
327 if let Some(only_buyer) = price_info.1 {
328 ensure!(only_buyer == buyer, Error::<T, I>::NoPermission);
329 }
330
331 T::Currency::transfer(
332 &buyer,
333 &details.owner,
334 price_info.0,
335 ExistenceRequirement::KeepAlive,
336 )?;
337
338 let old_owner = details.owner.clone();
339
340 Self::do_transfer(collection.clone(), item, buyer.clone(), |_, _| Ok(()))?;
341
342 Self::deposit_event(Event::ItemBought {
343 collection,
344 item,
345 price: price_info.0,
346 seller: old_owner,
347 buyer,
348 });
349
350 Ok(())
351 }
352}