1#![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 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}