1use crate::*;
22use frame_support::{pallet_prelude::*, traits::ExistenceRequirement};
23
24impl<T: Config<I>, I: 'static> Pallet<T, I> {
25 pub fn do_mint(
45 collection: T::CollectionId,
46 item: T::ItemId,
47 maybe_depositor: Option<T::AccountId>,
48 mint_to: T::AccountId,
49 item_config: ItemConfig,
50 with_details_and_config: impl FnOnce(
51 &CollectionDetailsFor<T, I>,
52 &CollectionConfigFor<T, I>,
53 ) -> DispatchResult,
54 ) -> DispatchResult {
55 ensure!(!Item::<T, I>::contains_key(collection, item), Error::<T, I>::AlreadyExists);
56
57 Collection::<T, I>::try_mutate(
58 &collection,
59 |maybe_collection_details| -> DispatchResult {
60 let collection_details =
61 maybe_collection_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
62
63 let collection_config = Self::get_collection_config(&collection)?;
64 with_details_and_config(collection_details, &collection_config)?;
65
66 if let Some(max_supply) = collection_config.max_supply {
67 ensure!(collection_details.items < max_supply, Error::<T, I>::MaxSupplyReached);
68 }
69
70 collection_details.items.saturating_inc();
71
72 let collection_config = Self::get_collection_config(&collection)?;
73 let deposit_amount = match collection_config
74 .is_setting_enabled(CollectionSetting::DepositRequired)
75 {
76 true => T::ItemDeposit::get(),
77 false => Zero::zero(),
78 };
79 let deposit_account = match maybe_depositor {
80 None => collection_details.owner.clone(),
81 Some(depositor) => depositor,
82 };
83
84 let item_owner = mint_to.clone();
85 Account::<T, I>::insert((&item_owner, &collection, &item), ());
86
87 if let Ok(existing_config) = ItemConfigOf::<T, I>::try_get(&collection, &item) {
88 ensure!(existing_config == item_config, Error::<T, I>::InconsistentItemConfig);
89 } else {
90 ItemConfigOf::<T, I>::insert(&collection, &item, item_config);
91 collection_details.item_configs.saturating_inc();
92 }
93
94 T::Currency::reserve(&deposit_account, deposit_amount)?;
95
96 let deposit = ItemDeposit { account: deposit_account, amount: deposit_amount };
97 let details = ItemDetails {
98 owner: item_owner,
99 approvals: ApprovalsOf::<T, I>::default(),
100 deposit,
101 };
102 Item::<T, I>::insert(&collection, &item, details);
103 Ok(())
104 },
105 )?;
106
107 Self::deposit_event(Event::Issued { collection, item, owner: mint_to });
108 Ok(())
109 }
110
111 pub(crate) fn do_mint_pre_signed(
125 mint_to: T::AccountId,
126 mint_data: PreSignedMintOf<T, I>,
127 signer: T::AccountId,
128 ) -> DispatchResult {
129 let PreSignedMint {
130 collection,
131 item,
132 attributes,
133 metadata,
134 deadline,
135 only_account,
136 mint_price,
137 } = mint_data;
138 let metadata = Self::construct_metadata(metadata)?;
139
140 ensure!(
141 attributes.len() <= T::MaxAttributesPerCall::get() as usize,
142 Error::<T, I>::MaxAttributesLimitReached
143 );
144 if let Some(account) = only_account {
145 ensure!(account == mint_to, Error::<T, I>::WrongOrigin);
146 }
147
148 let now = T::BlockNumberProvider::current_block_number();
149 ensure!(deadline >= now, Error::<T, I>::DeadlineExpired);
150
151 ensure!(
152 Self::has_role(&collection, &signer, CollectionRole::Issuer),
153 Error::<T, I>::NoPermission
154 );
155
156 let item_config = ItemConfig { settings: Self::get_default_item_settings(&collection)? };
157 Self::do_mint(
158 collection,
159 item,
160 Some(mint_to.clone()),
161 mint_to.clone(),
162 item_config,
163 |collection_details, _| {
164 if let Some(price) = mint_price {
165 T::Currency::transfer(
166 &mint_to,
167 &collection_details.owner,
168 price,
169 ExistenceRequirement::KeepAlive,
170 )?;
171 }
172 Ok(())
173 },
174 )?;
175 let admin_account = Self::find_account_by_role(&collection, CollectionRole::Admin);
176 if let Some(admin_account) = admin_account {
177 for (key, value) in attributes {
178 Self::do_set_attribute(
179 admin_account.clone(),
180 collection,
181 Some(item),
182 AttributeNamespace::CollectionOwner,
183 Self::construct_attribute_key(key)?,
184 Self::construct_attribute_value(value)?,
185 mint_to.clone(),
186 )?;
187 }
188 if !metadata.len().is_zero() {
189 Self::do_set_item_metadata(
190 Some(admin_account.clone()),
191 collection,
192 item,
193 metadata,
194 Some(mint_to.clone()),
195 )?;
196 }
197 }
198 Ok(())
199 }
200
201 pub fn do_burn(
209 collection: T::CollectionId,
210 item: T::ItemId,
211 with_details: impl FnOnce(&ItemDetailsFor<T, I>) -> DispatchResult,
212 ) -> DispatchResult {
213 ensure!(!T::Locker::is_locked(collection, item), Error::<T, I>::ItemLocked);
214 ensure!(
215 !Self::has_system_attribute(&collection, &item, PalletAttributes::TransferDisabled)?,
216 Error::<T, I>::ItemLocked
217 );
218 let item_config = Self::get_item_config(&collection, &item)?;
219 let remove_config = !item_config.has_disabled_settings();
222 let owner = Collection::<T, I>::try_mutate(
223 &collection,
224 |maybe_collection_details| -> Result<T::AccountId, DispatchError> {
225 let collection_details =
226 maybe_collection_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
227 let details = Item::<T, I>::get(&collection, &item)
228 .ok_or(Error::<T, I>::UnknownCollection)?;
229 with_details(&details)?;
230
231 T::Currency::unreserve(&details.deposit.account, details.deposit.amount);
233 collection_details.items.saturating_dec();
234
235 if remove_config {
236 collection_details.item_configs.saturating_dec();
237 }
238
239 if item_config.is_setting_enabled(ItemSetting::UnlockedMetadata) {
241 if let Some(metadata) = ItemMetadataOf::<T, I>::take(&collection, &item) {
242 let depositor_account =
243 metadata.deposit.account.unwrap_or(collection_details.owner.clone());
244
245 T::Currency::unreserve(&depositor_account, metadata.deposit.amount);
246 collection_details.item_metadatas.saturating_dec();
247
248 if depositor_account == collection_details.owner {
249 collection_details
250 .owner_deposit
251 .saturating_reduce(metadata.deposit.amount);
252 }
253 }
254 }
255
256 Ok(details.owner)
257 },
258 )?;
259
260 Item::<T, I>::remove(&collection, &item);
261 Account::<T, I>::remove((&owner, &collection, &item));
262 ItemPriceOf::<T, I>::remove(&collection, &item);
263 PendingSwapOf::<T, I>::remove(&collection, &item);
264 ItemAttributesApprovalsOf::<T, I>::remove(&collection, &item);
265
266 if remove_config {
267 ItemConfigOf::<T, I>::remove(&collection, &item);
268 }
269
270 Self::deposit_event(Event::Burned { collection, item, owner });
271 Ok(())
272 }
273}