referrerpolicy=no-referrer-when-downgrade

pallet_nfts/
benchmarking.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Nfts pallet benchmarking.
19
20#![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	// compare to the last event record
196	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}