1#![cfg(feature = "runtime-benchmarks")]
21
22use super::*;
23use enumflags2::{BitFlag, BitFlags};
24use frame_benchmarking::v1::{
25 account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, BenchmarkError,
26};
27use frame_support::{
28 assert_ok,
29 traits::{EnsureOrigin, Get, UnfilteredDispatchable},
30 BoundedVec,
31};
32use frame_system::RawOrigin as SystemOrigin;
33use sp_runtime::traits::{Bounded, One};
34
35use crate::Pallet as Nfts;
36
37const SEED: u32 = 0;
38
39fn create_collection<T: Config<I>, I: 'static>(
40) -> (T::CollectionId, T::AccountId, AccountIdLookupOf<T>) {
41 let caller: T::AccountId = whitelisted_caller();
42 let caller_lookup = T::Lookup::unlookup(caller.clone());
43 let collection = T::Helper::collection(0);
44 T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
45 assert_ok!(Nfts::<T, I>::force_create(
46 SystemOrigin::Root.into(),
47 caller_lookup.clone(),
48 default_collection_config::<T, I>()
49 ));
50 (collection, caller, caller_lookup)
51}
52
53fn add_collection_metadata<T: Config<I>, I: 'static>() -> (T::AccountId, AccountIdLookupOf<T>) {
54 let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
55 if caller != whitelisted_caller() {
56 whitelist_account!(caller);
57 }
58 let caller_lookup = T::Lookup::unlookup(caller.clone());
59 assert_ok!(Nfts::<T, I>::set_collection_metadata(
60 SystemOrigin::Signed(caller.clone()).into(),
61 T::Helper::collection(0),
62 vec![0; T::StringLimit::get() as usize].try_into().unwrap(),
63 ));
64 (caller, caller_lookup)
65}
66
67fn mint_item<T: Config<I>, I: 'static>(
68 index: u16,
69) -> (T::ItemId, T::AccountId, AccountIdLookupOf<T>) {
70 let item = T::Helper::item(index);
71 let collection = T::Helper::collection(0);
72 let caller = Collection::<T, I>::get(collection).unwrap().owner;
73 if caller != whitelisted_caller() {
74 whitelist_account!(caller);
75 }
76 let caller_lookup = T::Lookup::unlookup(caller.clone());
77 let item_exists = Item::<T, I>::contains_key(&collection, &item);
78 let item_config = ItemConfigOf::<T, I>::get(&collection, &item);
79 if item_exists {
80 return (item, caller, caller_lookup)
81 } else if let Some(item_config) = item_config {
82 assert_ok!(Nfts::<T, I>::force_mint(
83 SystemOrigin::Signed(caller.clone()).into(),
84 collection,
85 item,
86 caller_lookup.clone(),
87 item_config,
88 ));
89 } else {
90 assert_ok!(Nfts::<T, I>::mint(
91 SystemOrigin::Signed(caller.clone()).into(),
92 collection,
93 item,
94 caller_lookup.clone(),
95 None,
96 ));
97 }
98 (item, caller, caller_lookup)
99}
100
101fn lock_item<T: Config<I>, I: 'static>(
102 index: u16,
103) -> (T::ItemId, T::AccountId, AccountIdLookupOf<T>) {
104 let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
105 if caller != whitelisted_caller() {
106 whitelist_account!(caller);
107 }
108 let caller_lookup = T::Lookup::unlookup(caller.clone());
109 let item = T::Helper::item(index);
110 assert_ok!(Nfts::<T, I>::lock_item_transfer(
111 SystemOrigin::Signed(caller.clone()).into(),
112 T::Helper::collection(0),
113 item,
114 ));
115 (item, caller, caller_lookup)
116}
117
118fn burn_item<T: Config<I>, I: 'static>(
119 index: u16,
120) -> (T::ItemId, T::AccountId, AccountIdLookupOf<T>) {
121 let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
122 if caller != whitelisted_caller() {
123 whitelist_account!(caller);
124 }
125 let caller_lookup = T::Lookup::unlookup(caller.clone());
126 let item = T::Helper::item(index);
127 assert_ok!(Nfts::<T, I>::burn(
128 SystemOrigin::Signed(caller.clone()).into(),
129 T::Helper::collection(0),
130 item,
131 ));
132 (item, caller, caller_lookup)
133}
134
135fn add_item_metadata<T: Config<I>, I: 'static>(
136 item: T::ItemId,
137) -> (T::AccountId, AccountIdLookupOf<T>) {
138 let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
139 if caller != whitelisted_caller() {
140 whitelist_account!(caller);
141 }
142 let caller_lookup = T::Lookup::unlookup(caller.clone());
143 assert_ok!(Nfts::<T, I>::set_metadata(
144 SystemOrigin::Signed(caller.clone()).into(),
145 T::Helper::collection(0),
146 item,
147 vec![0; T::StringLimit::get() as usize].try_into().unwrap(),
148 ));
149 (caller, caller_lookup)
150}
151
152fn add_item_attribute<T: Config<I>, I: 'static>(
153 item: T::ItemId,
154) -> (BoundedVec<u8, T::KeyLimit>, T::AccountId, AccountIdLookupOf<T>) {
155 let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
156 if caller != whitelisted_caller() {
157 whitelist_account!(caller);
158 }
159 let caller_lookup = T::Lookup::unlookup(caller.clone());
160 let key: BoundedVec<_, _> = vec![0; T::KeyLimit::get() as usize].try_into().unwrap();
161 assert_ok!(Nfts::<T, I>::set_attribute(
162 SystemOrigin::Signed(caller.clone()).into(),
163 T::Helper::collection(0),
164 Some(item),
165 AttributeNamespace::CollectionOwner,
166 key.clone(),
167 vec![0; T::ValueLimit::get() as usize].try_into().unwrap(),
168 ));
169 (key, caller, caller_lookup)
170}
171
172fn add_collection_attribute<T: Config<I>, I: 'static>(
173 i: u16,
174) -> (BoundedVec<u8, T::KeyLimit>, T::AccountId, AccountIdLookupOf<T>) {
175 let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
176 if caller != whitelisted_caller() {
177 whitelist_account!(caller);
178 }
179 let caller_lookup = T::Lookup::unlookup(caller.clone());
180 let key: BoundedVec<_, _> = make_filled_vec(i, T::KeyLimit::get() as usize).try_into().unwrap();
181 assert_ok!(Nfts::<T, I>::set_attribute(
182 SystemOrigin::Signed(caller.clone()).into(),
183 T::Helper::collection(0),
184 None,
185 AttributeNamespace::CollectionOwner,
186 key.clone(),
187 vec![0; T::ValueLimit::get() as usize].try_into().unwrap(),
188 ));
189 (key, caller, caller_lookup)
190}
191
192fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
193 let events = frame_system::Pallet::<T>::events();
194 let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
195 let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
197 assert_eq!(event, &system_event);
198}
199
200fn make_collection_config<T: Config<I>, I: 'static>(
201 disable_settings: BitFlags<CollectionSetting>,
202) -> CollectionConfigFor<T, I> {
203 CollectionConfig {
204 settings: CollectionSettings::from_disabled(disable_settings),
205 max_supply: None,
206 mint_settings: MintSettings::default(),
207 }
208}
209
210fn default_collection_config<T: Config<I>, I: 'static>() -> CollectionConfigFor<T, I> {
211 make_collection_config::<T, I>(CollectionSetting::empty())
212}
213
214fn default_item_config() -> ItemConfig {
215 ItemConfig { settings: ItemSettings::all_enabled() }
216}
217
218fn make_filled_vec(value: u16, length: usize) -> Vec<u8> {
219 let mut vec = vec![0u8; length];
220 let mut s = Vec::from(value.to_be_bytes());
221 vec.truncate(length - s.len());
222 vec.append(&mut s);
223 vec
224}
225
226benchmarks_instance_pallet! {
227 create {
228 let collection = T::Helper::collection(0);
229 let origin = T::CreateOrigin::try_successful_origin(&collection)
230 .map_err(|_| BenchmarkError::Weightless)?;
231 let caller = T::CreateOrigin::ensure_origin(origin.clone(), &collection).unwrap();
232 whitelist_account!(caller);
233 let admin = T::Lookup::unlookup(caller.clone());
234 T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
235 let call = Call::<T, I>::create { admin, config: default_collection_config::<T, I>() };
236 }: { call.dispatch_bypass_filter(origin)? }
237 verify {
238 assert_last_event::<T, I>(Event::NextCollectionIdIncremented { next_id: Some(T::Helper::collection(1)) }.into());
239 }
240
241 force_create {
242 let caller: T::AccountId = whitelisted_caller();
243 let caller_lookup = T::Lookup::unlookup(caller.clone());
244 }: _(SystemOrigin::Root, caller_lookup, default_collection_config::<T, I>())
245 verify {
246 assert_last_event::<T, I>(Event::NextCollectionIdIncremented { next_id: Some(T::Helper::collection(1)) }.into());
247 }
248
249 destroy {
250 let m in 0 .. 1_000;
251 let c in 0 .. 1_000;
252 let a in 0 .. 1_000;
253
254 let (collection, caller, _) = create_collection::<T, I>();
255 add_collection_metadata::<T, I>();
256 for i in 0..m {
257 mint_item::<T, I>(i as u16);
258 add_item_metadata::<T, I>(T::Helper::item(i as u16));
259 lock_item::<T, I>(i as u16);
260 burn_item::<T, I>(i as u16);
261 }
262 for i in 0..c {
263 mint_item::<T, I>(i as u16);
264 lock_item::<T, I>(i as u16);
265 burn_item::<T, I>(i as u16);
266 }
267 for i in 0..a {
268 add_collection_attribute::<T, I>(i as u16);
269 }
270 let witness = Collection::<T, I>::get(collection).unwrap().destroy_witness();
271 }: _(SystemOrigin::Signed(caller), collection, witness)
272 verify {
273 assert_last_event::<T, I>(Event::Destroyed { collection }.into());
274 }
275
276 mint {
277 let (collection, caller, caller_lookup) = create_collection::<T, I>();
278 let item = T::Helper::item(0);
279 }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, None)
280 verify {
281 assert_last_event::<T, I>(Event::Issued { collection, item, owner: caller }.into());
282 }
283
284 force_mint {
285 let (collection, caller, caller_lookup) = create_collection::<T, I>();
286 let item = T::Helper::item(0);
287 }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, default_item_config())
288 verify {
289 assert_last_event::<T, I>(Event::Issued { collection, item, owner: caller }.into());
290 }
291
292 burn {
293 let (collection, caller, _) = create_collection::<T, I>();
294 let (item, ..) = mint_item::<T, I>(0);
295 }: _(SystemOrigin::Signed(caller.clone()), collection, item)
296 verify {
297 assert_last_event::<T, I>(Event::Burned { collection, item, owner: caller }.into());
298 }
299
300 transfer {
301 let (collection, caller, _) = create_collection::<T, I>();
302 let (item, ..) = mint_item::<T, I>(0);
303
304 let target: T::AccountId = account("target", 0, SEED);
305 let target_lookup = T::Lookup::unlookup(target.clone());
306 T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
307 }: _(SystemOrigin::Signed(caller.clone()), collection, item, target_lookup)
308 verify {
309 assert_last_event::<T, I>(Event::Transferred { collection, item, from: caller, to: target }.into());
310 }
311
312 redeposit {
313 let i in 0 .. 5_000;
314 let (collection, caller, _) = create_collection::<T, I>();
315 let items = (0..i).map(|x| mint_item::<T, I>(x as u16).0).collect::<Vec<_>>();
316 Nfts::<T, I>::force_collection_config(
317 SystemOrigin::Root.into(),
318 collection,
319 make_collection_config::<T, I>(CollectionSetting::DepositRequired.into()),
320 )?;
321 }: _(SystemOrigin::Signed(caller.clone()), collection, items.clone())
322 verify {
323 assert_last_event::<T, I>(Event::Redeposited { collection, successful_items: items }.into());
324 }
325
326 lock_item_transfer {
327 let (collection, caller, _) = create_collection::<T, I>();
328 let (item, ..) = mint_item::<T, I>(0);
329 }: _(SystemOrigin::Signed(caller.clone()), T::Helper::collection(0), T::Helper::item(0))
330 verify {
331 assert_last_event::<T, I>(Event::ItemTransferLocked { collection: T::Helper::collection(0), item: T::Helper::item(0) }.into());
332 }
333
334 unlock_item_transfer {
335 let (collection, caller, _) = create_collection::<T, I>();
336 let (item, ..) = mint_item::<T, I>(0);
337 Nfts::<T, I>::lock_item_transfer(
338 SystemOrigin::Signed(caller.clone()).into(),
339 collection,
340 item,
341 )?;
342 }: _(SystemOrigin::Signed(caller.clone()), collection, item)
343 verify {
344 assert_last_event::<T, I>(Event::ItemTransferUnlocked { collection, item }.into());
345 }
346
347 lock_collection {
348 let (collection, caller, _) = create_collection::<T, I>();
349 let lock_settings = CollectionSettings::from_disabled(
350 CollectionSetting::TransferableItems |
351 CollectionSetting::UnlockedMetadata |
352 CollectionSetting::UnlockedAttributes |
353 CollectionSetting::UnlockedMaxSupply,
354 );
355 }: _(SystemOrigin::Signed(caller.clone()), collection, lock_settings)
356 verify {
357 assert_last_event::<T, I>(Event::CollectionLocked { collection }.into());
358 }
359
360 transfer_ownership {
361 let (collection, caller, _) = create_collection::<T, I>();
362 let target: T::AccountId = account("target", 0, SEED);
363 let target_lookup = T::Lookup::unlookup(target.clone());
364 T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
365 let origin = SystemOrigin::Signed(target.clone()).into();
366 Nfts::<T, I>::set_accept_ownership(origin, Some(collection))?;
367 }: _(SystemOrigin::Signed(caller), collection, target_lookup)
368 verify {
369 assert_last_event::<T, I>(Event::OwnerChanged { collection, new_owner: target }.into());
370 }
371
372 set_team {
373 let (collection, caller, _) = create_collection::<T, I>();
374 let target0 = Some(T::Lookup::unlookup(account("target", 0, SEED)));
375 let target1 = Some(T::Lookup::unlookup(account("target", 1, SEED)));
376 let target2 = Some(T::Lookup::unlookup(account("target", 2, SEED)));
377 }: _(SystemOrigin::Signed(caller), collection, target0, target1, target2)
378 verify {
379 assert_last_event::<T, I>(Event::TeamChanged{
380 collection,
381 issuer: Some(account("target", 0, SEED)),
382 admin: Some(account("target", 1, SEED)),
383 freezer: Some(account("target", 2, SEED)),
384 }.into());
385 }
386
387 force_collection_owner {
388 let (collection, _, _) = create_collection::<T, I>();
389 let origin =
390 T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
391 let target: T::AccountId = account("target", 0, SEED);
392 let target_lookup = T::Lookup::unlookup(target.clone());
393 T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
394 let call = Call::<T, I>::force_collection_owner {
395 collection,
396 owner: target_lookup,
397 };
398 }: { call.dispatch_bypass_filter(origin)? }
399 verify {
400 assert_last_event::<T, I>(Event::OwnerChanged { collection, new_owner: target }.into());
401 }
402
403 force_collection_config {
404 let (collection, caller, _) = create_collection::<T, I>();
405 let origin =
406 T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
407 let call = Call::<T, I>::force_collection_config {
408 collection,
409 config: make_collection_config::<T, I>(CollectionSetting::DepositRequired.into()),
410 };
411 }: { call.dispatch_bypass_filter(origin)? }
412 verify {
413 assert_last_event::<T, I>(Event::CollectionConfigChanged { collection }.into());
414 }
415
416 lock_item_properties {
417 let (collection, caller, _) = create_collection::<T, I>();
418 let (item, ..) = mint_item::<T, I>(0);
419 let lock_metadata = true;
420 let lock_attributes = true;
421 }: _(SystemOrigin::Signed(caller), collection, item, lock_metadata, lock_attributes)
422 verify {
423 assert_last_event::<T, I>(Event::ItemPropertiesLocked { collection, item, lock_metadata, lock_attributes }.into());
424 }
425
426 set_attribute {
427 let key: BoundedVec<_, _> = vec![0u8; T::KeyLimit::get() as usize].try_into().unwrap();
428 let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap();
429
430 let (collection, caller, _) = create_collection::<T, I>();
431 let (item, ..) = mint_item::<T, I>(0);
432 }: _(SystemOrigin::Signed(caller), collection, Some(item), AttributeNamespace::CollectionOwner, key.clone(), value.clone())
433 verify {
434 assert_last_event::<T, I>(
435 Event::AttributeSet {
436 collection,
437 maybe_item: Some(item),
438 namespace: AttributeNamespace::CollectionOwner,
439 key,
440 value,
441 }
442 .into(),
443 );
444 }
445
446 force_set_attribute {
447 let key: BoundedVec<_, _> = vec![0u8; T::KeyLimit::get() as usize].try_into().unwrap();
448 let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap();
449
450 let (collection, caller, _) = create_collection::<T, I>();
451 let (item, ..) = mint_item::<T, I>(0);
452 }: _(SystemOrigin::Root, Some(caller), collection, Some(item), AttributeNamespace::CollectionOwner, key.clone(), value.clone())
453 verify {
454 assert_last_event::<T, I>(
455 Event::AttributeSet {
456 collection,
457 maybe_item: Some(item),
458 namespace: AttributeNamespace::CollectionOwner,
459 key,
460 value,
461 }
462 .into(),
463 );
464 }
465
466 clear_attribute {
467 let (collection, caller, _) = create_collection::<T, I>();
468 let (item, ..) = mint_item::<T, I>(0);
469 add_item_metadata::<T, I>(item);
470 let (key, ..) = add_item_attribute::<T, I>(item);
471 }: _(SystemOrigin::Signed(caller), collection, Some(item), AttributeNamespace::CollectionOwner, key.clone())
472 verify {
473 assert_last_event::<T, I>(
474 Event::AttributeCleared {
475 collection,
476 maybe_item: Some(item),
477 namespace: AttributeNamespace::CollectionOwner,
478 key,
479 }.into(),
480 );
481 }
482
483 approve_item_attributes {
484 let (collection, caller, _) = create_collection::<T, I>();
485 let (item, ..) = mint_item::<T, I>(0);
486 let target: T::AccountId = account("target", 0, SEED);
487 let target_lookup = T::Lookup::unlookup(target.clone());
488 }: _(SystemOrigin::Signed(caller), collection, item, target_lookup)
489 verify {
490 assert_last_event::<T, I>(
491 Event::ItemAttributesApprovalAdded {
492 collection,
493 item,
494 delegate: target,
495 }
496 .into(),
497 );
498 }
499
500 cancel_item_attributes_approval {
501 let n in 0 .. 1_000;
502
503 let (collection, caller, _) = create_collection::<T, I>();
504 let (item, ..) = mint_item::<T, I>(0);
505 let target: T::AccountId = account("target", 0, SEED);
506 let target_lookup = T::Lookup::unlookup(target.clone());
507 Nfts::<T, I>::approve_item_attributes(
508 SystemOrigin::Signed(caller.clone()).into(),
509 collection,
510 item,
511 target_lookup.clone(),
512 )?;
513 T::Currency::make_free_balance_be(&target, DepositBalanceOf::<T, I>::max_value());
514 let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap();
515 for i in 0..n {
516 let key = make_filled_vec(i as u16, T::KeyLimit::get() as usize);
517 Nfts::<T, I>::set_attribute(
518 SystemOrigin::Signed(target.clone()).into(),
519 T::Helper::collection(0),
520 Some(item),
521 AttributeNamespace::Account(target.clone()),
522 key.try_into().unwrap(),
523 value.clone(),
524 )?;
525 }
526 let witness = CancelAttributesApprovalWitness { account_attributes: n };
527 }: _(SystemOrigin::Signed(caller), collection, item, target_lookup, witness)
528 verify {
529 assert_last_event::<T, I>(
530 Event::ItemAttributesApprovalRemoved {
531 collection,
532 item,
533 delegate: target,
534 }
535 .into(),
536 );
537 }
538
539 set_metadata {
540 let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap();
541
542 let (collection, caller, _) = create_collection::<T, I>();
543 let (item, ..) = mint_item::<T, I>(0);
544 }: _(SystemOrigin::Signed(caller), collection, item, data.clone())
545 verify {
546 assert_last_event::<T, I>(Event::ItemMetadataSet { collection, item, data }.into());
547 }
548
549 clear_metadata {
550 let (collection, caller, _) = create_collection::<T, I>();
551 let (item, ..) = mint_item::<T, I>(0);
552 add_item_metadata::<T, I>(item);
553 }: _(SystemOrigin::Signed(caller), collection, item)
554 verify {
555 assert_last_event::<T, I>(Event::ItemMetadataCleared { collection, item }.into());
556 }
557
558 set_collection_metadata {
559 let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap();
560
561 let (collection, caller, _) = create_collection::<T, I>();
562 }: _(SystemOrigin::Signed(caller), collection, data.clone())
563 verify {
564 assert_last_event::<T, I>(Event::CollectionMetadataSet { collection, data }.into());
565 }
566
567 clear_collection_metadata {
568 let (collection, caller, _) = create_collection::<T, I>();
569 add_collection_metadata::<T, I>();
570 }: _(SystemOrigin::Signed(caller), collection)
571 verify {
572 assert_last_event::<T, I>(Event::CollectionMetadataCleared { collection }.into());
573 }
574
575 approve_transfer {
576 let (collection, caller, _) = create_collection::<T, I>();
577 let (item, ..) = mint_item::<T, I>(0);
578 let delegate: T::AccountId = account("delegate", 0, SEED);
579 let delegate_lookup = T::Lookup::unlookup(delegate.clone());
580 let deadline = BlockNumberFor::<T, I>::max_value();
581 }: _(SystemOrigin::Signed(caller.clone()), collection, item, delegate_lookup, Some(deadline))
582 verify {
583 assert_last_event::<T, I>(Event::TransferApproved { collection, item, owner: caller, delegate, deadline: Some(deadline) }.into());
584 }
585
586 cancel_approval {
587 let (collection, caller, _) = create_collection::<T, I>();
588 let (item, ..) = mint_item::<T, I>(0);
589 let delegate: T::AccountId = account("delegate", 0, SEED);
590 let delegate_lookup = T::Lookup::unlookup(delegate.clone());
591 let origin = SystemOrigin::Signed(caller.clone()).into();
592 let deadline = BlockNumberFor::<T, I>::max_value();
593 Nfts::<T, I>::approve_transfer(origin, collection, item, delegate_lookup.clone(), Some(deadline))?;
594 }: _(SystemOrigin::Signed(caller.clone()), collection, item, delegate_lookup)
595 verify {
596 assert_last_event::<T, I>(Event::ApprovalCancelled { collection, item, owner: caller, delegate }.into());
597 }
598
599 clear_all_transfer_approvals {
600 let (collection, caller, _) = create_collection::<T, I>();
601 let (item, ..) = mint_item::<T, I>(0);
602 let delegate: T::AccountId = account("delegate", 0, SEED);
603 let delegate_lookup = T::Lookup::unlookup(delegate.clone());
604 let origin = SystemOrigin::Signed(caller.clone()).into();
605 let deadline = BlockNumberFor::<T, I>::max_value();
606 Nfts::<T, I>::approve_transfer(origin, collection, item, delegate_lookup.clone(), Some(deadline))?;
607 }: _(SystemOrigin::Signed(caller.clone()), collection, item)
608 verify {
609 assert_last_event::<T, I>(Event::AllApprovalsCancelled {collection, item, owner: caller}.into());
610 }
611
612 set_accept_ownership {
613 let caller: T::AccountId = whitelisted_caller();
614 T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
615 let collection = T::Helper::collection(0);
616 }: _(SystemOrigin::Signed(caller.clone()), Some(collection))
617 verify {
618 assert_last_event::<T, I>(Event::OwnershipAcceptanceChanged {
619 who: caller,
620 maybe_collection: Some(collection),
621 }.into());
622 }
623
624 set_collection_max_supply {
625 let (collection, caller, _) = create_collection::<T, I>();
626 }: _(SystemOrigin::Signed(caller.clone()), collection, u32::MAX)
627 verify {
628 assert_last_event::<T, I>(Event::CollectionMaxSupplySet {
629 collection,
630 max_supply: u32::MAX,
631 }.into());
632 }
633
634 update_mint_settings {
635 let (collection, caller, _) = create_collection::<T, I>();
636 let mint_settings = MintSettings {
637 mint_type: MintType::HolderOf(T::Helper::collection(0)),
638 start_block: Some(One::one()),
639 end_block: Some(One::one()),
640 price: Some(ItemPrice::<T, I>::from(1u32)),
641 default_item_settings: ItemSettings::all_enabled(),
642 };
643 }: _(SystemOrigin::Signed(caller.clone()), collection, mint_settings)
644 verify {
645 assert_last_event::<T, I>(Event::CollectionMintSettingsUpdated { collection }.into());
646 }
647
648 set_price {
649 let (collection, caller, _) = create_collection::<T, I>();
650 let (item, ..) = mint_item::<T, I>(0);
651 let delegate: T::AccountId = account("delegate", 0, SEED);
652 let delegate_lookup = T::Lookup::unlookup(delegate.clone());
653 let price = ItemPrice::<T, I>::from(100u32);
654 }: _(SystemOrigin::Signed(caller.clone()), collection, item, Some(price), Some(delegate_lookup))
655 verify {
656 assert_last_event::<T, I>(Event::ItemPriceSet {
657 collection,
658 item,
659 price,
660 whitelisted_buyer: Some(delegate),
661 }.into());
662 }
663
664 buy_item {
665 let (collection, seller, _) = create_collection::<T, I>();
666 let (item, ..) = mint_item::<T, I>(0);
667 let buyer: T::AccountId = account("buyer", 0, SEED);
668 let buyer_lookup = T::Lookup::unlookup(buyer.clone());
669 let price = ItemPrice::<T, I>::from(0u32);
670 let origin = SystemOrigin::Signed(seller.clone()).into();
671 Nfts::<T, I>::set_price(origin, collection, item, Some(price), Some(buyer_lookup))?;
672 T::Currency::make_free_balance_be(&buyer, DepositBalanceOf::<T, I>::max_value());
673 }: _(SystemOrigin::Signed(buyer.clone()), collection, item, price)
674 verify {
675 assert_last_event::<T, I>(Event::ItemBought {
676 collection,
677 item,
678 price,
679 seller,
680 buyer,
681 }.into());
682 }
683
684 pay_tips {
685 let n in 0 .. T::MaxTips::get() as u32;
686 let amount = BalanceOf::<T, I>::from(100u32);
687 let caller: T::AccountId = whitelisted_caller();
688 let collection = T::Helper::collection(0);
689 let item = T::Helper::item(0);
690 let tips: BoundedVec<_, _> = vec![
691 ItemTip
692 { collection, item, receiver: caller.clone(), amount }; n as usize
693 ].try_into().unwrap();
694 }: _(SystemOrigin::Signed(caller.clone()), tips)
695 verify {
696 if !n.is_zero() {
697 assert_last_event::<T, I>(Event::TipSent {
698 collection,
699 item,
700 sender: caller.clone(),
701 receiver: caller.clone(),
702 amount,
703 }.into());
704 }
705 }
706
707 create_swap {
708 let (collection, caller, _) = create_collection::<T, I>();
709 let (item1, ..) = mint_item::<T, I>(0);
710 let (item2, ..) = mint_item::<T, I>(1);
711 let price = ItemPrice::<T, I>::from(100u32);
712 let price_direction = PriceDirection::Receive;
713 let price_with_direction = PriceWithDirection { amount: price, direction: price_direction };
714 let duration = T::MaxDeadlineDuration::get();
715 T::BlockNumberProvider::set_block_number(One::one());
716 }: _(SystemOrigin::Signed(caller.clone()), collection, item1, collection, Some(item2), Some(price_with_direction.clone()), duration)
717 verify {
718 let current_block = T::BlockNumberProvider::current_block_number();
719 assert_last_event::<T, I>(Event::SwapCreated {
720 offered_collection: collection,
721 offered_item: item1,
722 desired_collection: collection,
723 desired_item: Some(item2),
724 price: Some(price_with_direction),
725 deadline: current_block.saturating_add(duration),
726 }.into());
727 }
728
729 cancel_swap {
730 let (collection, caller, _) = create_collection::<T, I>();
731 let (item1, ..) = mint_item::<T, I>(0);
732 let (item2, ..) = mint_item::<T, I>(1);
733 let price = ItemPrice::<T, I>::from(100u32);
734 let origin = SystemOrigin::Signed(caller.clone()).into();
735 let duration = T::MaxDeadlineDuration::get();
736 let price_direction = PriceDirection::Receive;
737 let price_with_direction = PriceWithDirection { amount: price, direction: price_direction };
738 T::BlockNumberProvider::set_block_number(One::one());
739 Nfts::<T, I>::create_swap(origin, collection, item1, collection, Some(item2), Some(price_with_direction.clone()), duration)?;
740 }: _(SystemOrigin::Signed(caller.clone()), collection, item1)
741 verify {
742 assert_last_event::<T, I>(Event::SwapCancelled {
743 offered_collection: collection,
744 offered_item: item1,
745 desired_collection: collection,
746 desired_item: Some(item2),
747 price: Some(price_with_direction),
748 deadline: duration.saturating_add(One::one()),
749 }.into());
750 }
751
752 claim_swap {
753 let (collection, caller, _) = create_collection::<T, I>();
754 let (item1, ..) = mint_item::<T, I>(0);
755 let (item2, ..) = mint_item::<T, I>(1);
756 let price = ItemPrice::<T, I>::from(0u32);
757 let price_direction = PriceDirection::Receive;
758 let price_with_direction = PriceWithDirection { amount: price, direction: price_direction };
759 let duration = T::MaxDeadlineDuration::get();
760 let target: T::AccountId = account("target", 0, SEED);
761 let target_lookup = T::Lookup::unlookup(target.clone());
762 T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
763 let origin = SystemOrigin::Signed(caller.clone());
764 T::BlockNumberProvider::set_block_number(One::one());
765 Nfts::<T, I>::transfer(origin.clone().into(), collection, item2, target_lookup)?;
766 Nfts::<T, I>::create_swap(
767 origin.clone().into(),
768 collection,
769 item1,
770 collection,
771 Some(item2),
772 Some(price_with_direction.clone()),
773 duration,
774 )?;
775 }: _(SystemOrigin::Signed(target.clone()), collection, item2, collection, item1, Some(price_with_direction.clone()))
776 verify {
777 let current_block = T::BlockNumberProvider::current_block_number();
778 assert_last_event::<T, I>(Event::SwapClaimed {
779 sent_collection: collection,
780 sent_item: item2,
781 sent_item_owner: target,
782 received_collection: collection,
783 received_item: item1,
784 received_item_owner: caller,
785 price: Some(price_with_direction),
786 deadline: duration.saturating_add(One::one()),
787 }.into());
788 }
789
790 mint_pre_signed {
791 let n in 0 .. T::MaxAttributesPerCall::get() as u32;
792 let (caller_public, caller) = T::Helper::signer();
793 T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
794 let caller_lookup = T::Lookup::unlookup(caller.clone());
795
796 let collection = T::Helper::collection(0);
797 let item = T::Helper::item(0);
798 assert_ok!(Nfts::<T, I>::force_create(
799 SystemOrigin::Root.into(),
800 caller_lookup.clone(),
801 default_collection_config::<T, I>()
802 ));
803
804 let metadata = vec![0u8; T::StringLimit::get() as usize];
805 let mut attributes = vec![];
806 let attribute_value = vec![0u8; T::ValueLimit::get() as usize];
807 for i in 0..n {
808 let attribute_key = make_filled_vec(i as u16, T::KeyLimit::get() as usize);
809 attributes.push((attribute_key, attribute_value.clone()));
810 }
811 let mint_data = PreSignedMint {
812 collection,
813 item,
814 attributes,
815 metadata: metadata.clone(),
816 only_account: None,
817 deadline: One::one(),
818 mint_price: Some(DepositBalanceOf::<T, I>::min_value()),
819 };
820 let message = Encode::encode(&mint_data);
821 let signature = T::Helper::sign(&caller_public, &message);
822
823 let target: T::AccountId = account("target", 0, SEED);
824 T::Currency::make_free_balance_be(&target, DepositBalanceOf::<T, I>::max_value());
825 T::BlockNumberProvider::set_block_number(One::one());
826 }: _(SystemOrigin::Signed(target.clone()), Box::new(mint_data), signature.into(), caller)
827 verify {
828 let metadata: BoundedVec<_, _> = metadata.try_into().unwrap();
829 assert_last_event::<T, I>(Event::ItemMetadataSet { collection, item, data: metadata }.into());
830 }
831
832 set_attributes_pre_signed {
833 let n in 0 .. T::MaxAttributesPerCall::get() as u32;
834 let (collection, _, _) = create_collection::<T, I>();
835
836 let item_owner: T::AccountId = account("item_owner", 0, SEED);
837 let item_owner_lookup = T::Lookup::unlookup(item_owner.clone());
838
839 let (signer_public, signer) = T::Helper::signer();
840
841 T::Currency::make_free_balance_be(&item_owner, DepositBalanceOf::<T, I>::max_value());
842
843 let item = T::Helper::item(0);
844 assert_ok!(Nfts::<T, I>::force_mint(
845 SystemOrigin::Root.into(),
846 collection,
847 item,
848 item_owner_lookup.clone(),
849 default_item_config(),
850 ));
851
852 let mut attributes = vec![];
853 let attribute_value = vec![0u8; T::ValueLimit::get() as usize];
854 for i in 0..n {
855 let attribute_key = make_filled_vec(i as u16, T::KeyLimit::get() as usize);
856 attributes.push((attribute_key, attribute_value.clone()));
857 }
858 let pre_signed_data = PreSignedAttributes {
859 collection,
860 item,
861 attributes,
862 namespace: AttributeNamespace::Account(signer.clone()),
863 deadline: One::one(),
864 };
865 let message = Encode::encode(&pre_signed_data);
866 let signature = T::Helper::sign(&signer_public, &message);
867
868 T::BlockNumberProvider::set_block_number(One::one());
869 }: _(SystemOrigin::Signed(item_owner.clone()), pre_signed_data, signature.into(), signer.clone())
870 verify {
871 assert_last_event::<T, I>(
872 Event::PreSignedAttributesSet {
873 collection,
874 item,
875 namespace: AttributeNamespace::Account(signer.clone()),
876 }
877 .into(),
878 );
879 }
880
881 impl_benchmark_test_suite!(Nfts, crate::mock::new_test_ext(), crate::mock::Test);
882}