referrerpolicy=no-referrer-when-downgrade

pallet_uniques/
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//! Uniques pallet benchmarking.
19
20#![cfg(feature = "runtime-benchmarks")]
21
22use super::*;
23use alloc::{vec, vec::Vec};
24use frame_benchmarking::v1::{
25	account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, BenchmarkError,
26};
27use frame_support::{
28	traits::{EnsureOrigin, Get, UnfilteredDispatchable},
29	BoundedVec,
30};
31use frame_system::RawOrigin as SystemOrigin;
32use sp_runtime::traits::Bounded;
33
34use crate::Pallet as Uniques;
35
36const SEED: u32 = 0;
37
38fn create_collection<T: Config<I>, I: 'static>(
39) -> (T::CollectionId, T::AccountId, AccountIdLookupOf<T>) {
40	let caller: T::AccountId = whitelisted_caller();
41	let caller_lookup = T::Lookup::unlookup(caller.clone());
42	let collection = T::Helper::collection(0);
43	T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
44	assert!(Uniques::<T, I>::force_create(
45		SystemOrigin::Root.into(),
46		collection.clone(),
47		caller_lookup.clone(),
48		false,
49	)
50	.is_ok());
51	(collection, caller, caller_lookup)
52}
53
54fn add_collection_metadata<T: Config<I>, I: 'static>() -> (T::AccountId, AccountIdLookupOf<T>) {
55	let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
56	if caller != whitelisted_caller() {
57		whitelist_account!(caller);
58	}
59	let caller_lookup = T::Lookup::unlookup(caller.clone());
60	assert!(Uniques::<T, I>::set_collection_metadata(
61		SystemOrigin::Signed(caller.clone()).into(),
62		T::Helper::collection(0),
63		vec![0; T::StringLimit::get() as usize].try_into().unwrap(),
64		false,
65	)
66	.is_ok());
67	(caller, caller_lookup)
68}
69
70fn mint_item<T: Config<I>, I: 'static>(
71	index: u16,
72) -> (T::ItemId, T::AccountId, AccountIdLookupOf<T>) {
73	let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().admin;
74	if caller != whitelisted_caller() {
75		whitelist_account!(caller);
76	}
77	let caller_lookup = T::Lookup::unlookup(caller.clone());
78	let item = T::Helper::item(index);
79	assert!(Uniques::<T, I>::mint(
80		SystemOrigin::Signed(caller.clone()).into(),
81		T::Helper::collection(0),
82		item,
83		caller_lookup.clone(),
84	)
85	.is_ok());
86	(item, caller, caller_lookup)
87}
88
89fn add_item_metadata<T: Config<I>, I: 'static>(
90	item: T::ItemId,
91) -> (T::AccountId, AccountIdLookupOf<T>) {
92	let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
93	if caller != whitelisted_caller() {
94		whitelist_account!(caller);
95	}
96	let caller_lookup = T::Lookup::unlookup(caller.clone());
97	assert!(Uniques::<T, I>::set_metadata(
98		SystemOrigin::Signed(caller.clone()).into(),
99		T::Helper::collection(0),
100		item,
101		vec![0; T::StringLimit::get() as usize].try_into().unwrap(),
102		false,
103	)
104	.is_ok());
105	(caller, caller_lookup)
106}
107
108fn add_item_attribute<T: Config<I>, I: 'static>(
109	item: T::ItemId,
110) -> (BoundedVec<u8, T::KeyLimit>, T::AccountId, AccountIdLookupOf<T>) {
111	let caller = Collection::<T, I>::get(T::Helper::collection(0)).unwrap().owner;
112	if caller != whitelisted_caller() {
113		whitelist_account!(caller);
114	}
115	let caller_lookup = T::Lookup::unlookup(caller.clone());
116	let key: BoundedVec<_, _> = vec![0; T::KeyLimit::get() as usize].try_into().unwrap();
117	assert!(Uniques::<T, I>::set_attribute(
118		SystemOrigin::Signed(caller.clone()).into(),
119		T::Helper::collection(0),
120		Some(item),
121		key.clone(),
122		vec![0; T::ValueLimit::get() as usize].try_into().unwrap(),
123	)
124	.is_ok());
125	(key, caller, caller_lookup)
126}
127
128fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
129	let events = frame_system::Pallet::<T>::events();
130	let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
131	// compare to the last event record
132	let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
133	assert_eq!(event, &system_event);
134}
135
136benchmarks_instance_pallet! {
137	create {
138		let collection = T::Helper::collection(0);
139		let origin = T::CreateOrigin::try_successful_origin(&collection)
140			.map_err(|_| BenchmarkError::Weightless)?;
141		let caller = T::CreateOrigin::ensure_origin(origin.clone(), &collection).unwrap();
142		whitelist_account!(caller);
143		let admin = T::Lookup::unlookup(caller.clone());
144		T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
145		let call = Call::<T, I>::create { collection, admin };
146	}: { call.dispatch_bypass_filter(origin)? }
147	verify {
148		assert_last_event::<T, I>(Event::Created { collection: T::Helper::collection(0), creator: caller.clone(), owner: caller }.into());
149	}
150
151	force_create {
152		let caller: T::AccountId = whitelisted_caller();
153		let caller_lookup = T::Lookup::unlookup(caller.clone());
154	}: _(SystemOrigin::Root, T::Helper::collection(0), caller_lookup, true)
155	verify {
156		assert_last_event::<T, I>(Event::ForceCreated { collection: T::Helper::collection(0), owner: caller }.into());
157	}
158
159	destroy {
160		let n in 0 .. 1_000;
161		let m in 0 .. 1_000;
162		let a in 0 .. 1_000;
163
164		let (collection, caller, caller_lookup) = create_collection::<T, I>();
165		add_collection_metadata::<T, I>();
166		for i in 0..n {
167			mint_item::<T, I>(i as u16);
168		}
169		for i in 0..m {
170			add_item_metadata::<T, I>(T::Helper::item(i as u16));
171		}
172		for i in 0..a {
173			add_item_attribute::<T, I>(T::Helper::item(i as u16));
174		}
175		let witness = Collection::<T, I>::get(collection.clone()).unwrap().destroy_witness();
176	}: _(SystemOrigin::Signed(caller), collection.clone(), witness)
177	verify {
178		assert_last_event::<T, I>(Event::Destroyed { collection: collection.clone() }.into());
179	}
180
181	mint {
182		let (collection, caller, caller_lookup) = create_collection::<T, I>();
183		let item = T::Helper::item(0);
184	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, caller_lookup)
185	verify {
186		assert_last_event::<T, I>(Event::Issued { collection: collection.clone(), item, owner: caller }.into());
187	}
188
189	burn {
190		let (collection, caller, caller_lookup) = create_collection::<T, I>();
191		let (item, ..) = mint_item::<T, I>(0);
192	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, Some(caller_lookup))
193	verify {
194		assert_last_event::<T, I>(Event::Burned { collection: collection.clone(), item, owner: caller }.into());
195	}
196
197	transfer {
198		let (collection, caller, caller_lookup) = create_collection::<T, I>();
199		let (item, ..) = mint_item::<T, I>(0);
200
201		let target: T::AccountId = account("target", 0, SEED);
202		let target_lookup = T::Lookup::unlookup(target.clone());
203	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, target_lookup)
204	verify {
205		assert_last_event::<T, I>(Event::Transferred { collection: collection.clone(), item, from: caller, to: target }.into());
206	}
207
208	redeposit {
209		let i in 0 .. 5_000;
210		let (collection, caller, caller_lookup) = create_collection::<T, I>();
211		let items = (0..i).map(|x| mint_item::<T, I>(x as u16).0).collect::<Vec<_>>();
212		Uniques::<T, I>::force_item_status(
213			SystemOrigin::Root.into(),
214			collection.clone(),
215			caller_lookup.clone(),
216			caller_lookup.clone(),
217			caller_lookup.clone(),
218			caller_lookup,
219			true,
220			false,
221		)?;
222	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), items.clone())
223	verify {
224		assert_last_event::<T, I>(Event::Redeposited { collection: collection.clone(), successful_items: items }.into());
225	}
226
227	freeze {
228		let (collection, caller, caller_lookup) = create_collection::<T, I>();
229		let (item, ..) = mint_item::<T, I>(0);
230	}: _(SystemOrigin::Signed(caller.clone()), T::Helper::collection(0), T::Helper::item(0))
231	verify {
232		assert_last_event::<T, I>(Event::Frozen { collection: T::Helper::collection(0), item: T::Helper::item(0) }.into());
233	}
234
235	thaw {
236		let (collection, caller, caller_lookup) = create_collection::<T, I>();
237		let (item, ..) = mint_item::<T, I>(0);
238		Uniques::<T, I>::freeze(
239			SystemOrigin::Signed(caller.clone()).into(),
240			collection.clone(),
241			item,
242		)?;
243	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item)
244	verify {
245		assert_last_event::<T, I>(Event::Thawed { collection: collection.clone(), item }.into());
246	}
247
248	freeze_collection {
249		let (collection, caller, caller_lookup) = create_collection::<T, I>();
250	}: _(SystemOrigin::Signed(caller.clone()), collection.clone())
251	verify {
252		assert_last_event::<T, I>(Event::CollectionFrozen { collection: collection.clone() }.into());
253	}
254
255	thaw_collection {
256		let (collection, caller, caller_lookup) = create_collection::<T, I>();
257		let origin = SystemOrigin::Signed(caller.clone()).into();
258		Uniques::<T, I>::freeze_collection(origin, collection.clone())?;
259	}: _(SystemOrigin::Signed(caller.clone()), collection.clone())
260	verify {
261		assert_last_event::<T, I>(Event::CollectionThawed { collection: collection.clone() }.into());
262	}
263
264	transfer_ownership {
265		let (collection, caller, _) = create_collection::<T, I>();
266		let target: T::AccountId = account("target", 0, SEED);
267		let target_lookup = T::Lookup::unlookup(target.clone());
268		T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance());
269		let origin = SystemOrigin::Signed(target.clone()).into();
270		Uniques::<T, I>::set_accept_ownership(origin, Some(collection.clone()))?;
271	}: _(SystemOrigin::Signed(caller), collection.clone(), target_lookup)
272	verify {
273		assert_last_event::<T, I>(Event::OwnerChanged { collection: collection.clone(), new_owner: target }.into());
274	}
275
276	set_team {
277		let (collection, caller, _) = create_collection::<T, I>();
278		let target0 = T::Lookup::unlookup(account("target", 0, SEED));
279		let target1 = T::Lookup::unlookup(account("target", 1, SEED));
280		let target2 = T::Lookup::unlookup(account("target", 2, SEED));
281	}: _(SystemOrigin::Signed(caller), collection.clone(), target0, target1, target2)
282	verify {
283		assert_last_event::<T, I>(Event::TeamChanged{
284			collection: collection.clone(),
285			issuer: account("target", 0, SEED),
286			admin: account("target", 1, SEED),
287			freezer: account("target", 2, SEED),
288		}.into());
289	}
290
291	force_item_status {
292		let (collection, caller, caller_lookup) = create_collection::<T, I>();
293		let origin =
294			T::ForceOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
295		let call = Call::<T, I>::force_item_status {
296			collection: collection.clone(),
297			owner: caller_lookup.clone(),
298			issuer: caller_lookup.clone(),
299			admin: caller_lookup.clone(),
300			freezer: caller_lookup,
301			free_holding: true,
302			is_frozen: false,
303		};
304	}: { call.dispatch_bypass_filter(origin)? }
305	verify {
306		assert_last_event::<T, I>(Event::ItemStatusChanged { collection: collection.clone() }.into());
307	}
308
309	set_attribute {
310		let key: BoundedVec<_, _> = vec![0u8; T::KeyLimit::get() as usize].try_into().unwrap();
311		let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap();
312
313		let (collection, caller, _) = create_collection::<T, I>();
314		let (item, ..) = mint_item::<T, I>(0);
315		add_item_metadata::<T, I>(item);
316	}: _(SystemOrigin::Signed(caller), collection.clone(), Some(item), key.clone(), value.clone())
317	verify {
318		assert_last_event::<T, I>(Event::AttributeSet { collection: collection.clone(), maybe_item: Some(item), key, value }.into());
319	}
320
321	clear_attribute {
322		let (collection, caller, _) = create_collection::<T, I>();
323		let (item, ..) = mint_item::<T, I>(0);
324		add_item_metadata::<T, I>(item);
325		let (key, ..) = add_item_attribute::<T, I>(item);
326	}: _(SystemOrigin::Signed(caller), collection.clone(), Some(item), key.clone())
327	verify {
328		assert_last_event::<T, I>(Event::AttributeCleared { collection: collection.clone(), maybe_item: Some(item), key }.into());
329	}
330
331	set_metadata {
332		let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap();
333
334		let (collection, caller, _) = create_collection::<T, I>();
335		let (item, ..) = mint_item::<T, I>(0);
336	}: _(SystemOrigin::Signed(caller), collection.clone(), item, data.clone(), false)
337	verify {
338		assert_last_event::<T, I>(Event::MetadataSet { collection: collection.clone(), item, data, is_frozen: false }.into());
339	}
340
341	clear_metadata {
342		let (collection, caller, _) = create_collection::<T, I>();
343		let (item, ..) = mint_item::<T, I>(0);
344		add_item_metadata::<T, I>(item);
345	}: _(SystemOrigin::Signed(caller), collection.clone(), item)
346	verify {
347		assert_last_event::<T, I>(Event::MetadataCleared { collection: collection.clone(), item }.into());
348	}
349
350	set_collection_metadata {
351		let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap();
352
353		let (collection, caller, _) = create_collection::<T, I>();
354	}: _(SystemOrigin::Signed(caller), collection.clone(), data.clone(), false)
355	verify {
356		assert_last_event::<T, I>(Event::CollectionMetadataSet { collection: collection.clone(), data, is_frozen: false }.into());
357	}
358
359	clear_collection_metadata {
360		let (collection, caller, _) = create_collection::<T, I>();
361		add_collection_metadata::<T, I>();
362	}: _(SystemOrigin::Signed(caller), collection.clone())
363	verify {
364		assert_last_event::<T, I>(Event::CollectionMetadataCleared { collection: collection.clone() }.into());
365	}
366
367	approve_transfer {
368		let (collection, caller, _) = create_collection::<T, I>();
369		let (item, ..) = mint_item::<T, I>(0);
370		let delegate: T::AccountId = account("delegate", 0, SEED);
371		let delegate_lookup = T::Lookup::unlookup(delegate.clone());
372	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, delegate_lookup)
373	verify {
374		assert_last_event::<T, I>(Event::ApprovedTransfer { collection: collection.clone(), item, owner: caller, delegate }.into());
375	}
376
377	cancel_approval {
378		let (collection, caller, _) = create_collection::<T, I>();
379		let (item, ..) = mint_item::<T, I>(0);
380		let delegate: T::AccountId = account("delegate", 0, SEED);
381		let delegate_lookup = T::Lookup::unlookup(delegate.clone());
382		let origin = SystemOrigin::Signed(caller.clone()).into();
383		Uniques::<T, I>::approve_transfer(origin, collection.clone(), item, delegate_lookup.clone())?;
384	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, Some(delegate_lookup))
385	verify {
386		assert_last_event::<T, I>(Event::ApprovalCancelled { collection: collection.clone(), item, owner: caller, delegate }.into());
387	}
388
389	set_accept_ownership {
390		let caller: T::AccountId = whitelisted_caller();
391		T::Currency::make_free_balance_be(&caller, DepositBalanceOf::<T, I>::max_value());
392		let collection = T::Helper::collection(0);
393	}: _(SystemOrigin::Signed(caller.clone()), Some(collection.clone()))
394	verify {
395		assert_last_event::<T, I>(Event::OwnershipAcceptanceChanged {
396			who: caller,
397			maybe_collection: Some(collection),
398		}.into());
399	}
400
401	set_collection_max_supply {
402		let (collection, caller, _) = create_collection::<T, I>();
403	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), u32::MAX)
404	verify {
405		assert_last_event::<T, I>(Event::CollectionMaxSupplySet {
406			collection: collection.clone(),
407			max_supply: u32::MAX,
408		}.into());
409	}
410
411	set_price {
412		let (collection, caller, _) = create_collection::<T, I>();
413		let (item, ..) = mint_item::<T, I>(0);
414		let delegate: T::AccountId = account("delegate", 0, SEED);
415		let delegate_lookup = T::Lookup::unlookup(delegate.clone());
416		let price = ItemPrice::<T, I>::from(100u32);
417	}: _(SystemOrigin::Signed(caller.clone()), collection.clone(), item, Some(price), Some(delegate_lookup))
418	verify {
419		assert_last_event::<T, I>(Event::ItemPriceSet {
420			collection: collection.clone(),
421			item,
422			price,
423			whitelisted_buyer: Some(delegate),
424		}.into());
425	}
426
427	buy_item {
428		let (collection, seller, _) = create_collection::<T, I>();
429		let (item, ..) = mint_item::<T, I>(0);
430		let buyer: T::AccountId = account("buyer", 0, SEED);
431		let buyer_lookup = T::Lookup::unlookup(buyer.clone());
432		let price = ItemPrice::<T, I>::from(0u32);
433		let origin = SystemOrigin::Signed(seller.clone()).into();
434		Uniques::<T, I>::set_price(origin, collection.clone(), item, Some(price), Some(buyer_lookup))?;
435		T::Currency::make_free_balance_be(&buyer, DepositBalanceOf::<T, I>::max_value());
436	}: _(SystemOrigin::Signed(buyer.clone()), collection.clone(), item, price)
437	verify {
438		assert_last_event::<T, I>(Event::ItemBought {
439			collection: collection.clone(),
440			item,
441			price,
442			seller,
443			buyer,
444		}.into());
445	}
446
447	impl_benchmark_test_suite!(Uniques, crate::mock::new_test_ext(), crate::mock::Test);
448}