1use alloc::{vec, vec::Vec};
21use frame_benchmarking::v2::*;
22use frame_election_provider_support::SortedListProvider;
23use frame_support::{
24 assert_ok, ensure,
25 traits::{
26 fungible::{Inspect, Mutate, Unbalanced},
27 tokens::Preservation,
28 Get, Imbalance,
29 },
30};
31use frame_system::RawOrigin as RuntimeOrigin;
32use pallet_nomination_pools::{
33 adapter::{Member, Pool, StakeStrategy, StakeStrategyType},
34 BalanceOf, BondExtra, BondedPoolInner, BondedPools, ClaimPermission, ClaimPermissions,
35 Commission, CommissionChangeRate, CommissionClaimPermission, ConfigOp, GlobalMaxCommission,
36 MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond,
37 Pallet as Pools, PoolId, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage,
38};
39use pallet_staking::MaxNominationsOf;
40use sp_runtime::{
41 traits::{Bounded, StaticLookup, Zero},
42 Perbill,
43};
44use sp_staking::{EraIndex, StakingUnchecked};
45use pallet_nomination_pools::Call;
47
48type CurrencyOf<T> = <T as pallet_nomination_pools::Config>::Currency;
49
50const USER_SEED: u32 = 0;
51const MAX_SPANS: u32 = 100;
52
53pub(crate) type VoterBagsListInstance = pallet_bags_list::Instance1;
54pub trait Config:
55 pallet_nomination_pools::Config
56 + pallet_staking::Config
57 + pallet_bags_list::Config<VoterBagsListInstance>
58{
59}
60
61pub struct Pallet<T: Config>(Pools<T>);
62
63fn create_funded_user_with_balance<T: pallet_nomination_pools::Config>(
64 string: &'static str,
65 n: u32,
66 balance: BalanceOf<T>,
67) -> T::AccountId {
68 let user = account(string, n, USER_SEED);
69 T::Currency::set_balance(&user, balance);
70 user
71}
72
73fn create_pool_account<T: pallet_nomination_pools::Config>(
76 n: u32,
77 balance: BalanceOf<T>,
78 commission: Option<Perbill>,
79) -> (T::AccountId, T::AccountId) {
80 let ed = CurrencyOf::<T>::minimum_balance();
81 let pool_creator: T::AccountId =
82 create_funded_user_with_balance::<T>("pool_creator", n, ed + balance * 2u32.into());
83 let pool_creator_lookup = T::Lookup::unlookup(pool_creator.clone());
84
85 Pools::<T>::create(
86 RuntimeOrigin::Signed(pool_creator.clone()).into(),
87 balance,
88 pool_creator_lookup.clone(),
89 pool_creator_lookup.clone(),
90 pool_creator_lookup,
91 )
92 .unwrap();
93
94 if let Some(c) = commission {
95 let pool_id = pallet_nomination_pools::LastPoolId::<T>::get();
96 Pools::<T>::set_commission(
97 RuntimeOrigin::Signed(pool_creator.clone()).into(),
98 pool_id,
99 Some((c, pool_creator.clone())),
100 )
101 .expect("pool just created, commission can be set by root; qed");
102 }
103
104 let pool_account = pallet_nomination_pools::BondedPools::<T>::iter()
105 .find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator)
106 .map(|(pool_id, _)| Pools::<T>::generate_bonded_account(pool_id))
107 .expect("pool_creator created a pool above");
108
109 (pool_creator, pool_account)
110}
111
112fn migrate_to_transfer_stake<T: Config>(pool_id: PoolId) {
113 if T::StakeAdapter::strategy_type() == StakeStrategyType::Transfer {
114 return;
116 }
117 let pool_acc = Pools::<T>::generate_bonded_account(pool_id);
118 T::StakeAdapter::remove_as_agent(Pool::from(pool_acc.clone()));
120
121 PoolMembers::<T>::iter()
123 .filter(|(_, member)| member.pool_id == pool_id)
124 .for_each(|(member_acc, member)| {
125 let member_balance = member.total_balance();
126 <T as pallet_nomination_pools::Config>::Currency::transfer(
127 &member_acc,
128 &pool_acc,
129 member_balance,
130 Preservation::Preserve,
131 )
132 .expect("member should have enough balance to transfer");
133 });
134
135 let _ = CurrencyOf::<T>::mint_into(&pool_acc, CurrencyOf::<T>::minimum_balance());
138
139 pallet_staking::Pallet::<T>::migrate_to_direct_staker(&pool_acc);
140}
141
142fn vote_to_balance<T: pallet_nomination_pools::Config>(
143 vote: u64,
144) -> Result<BalanceOf<T>, &'static str> {
145 vote.try_into().map_err(|_| "could not convert u64 to Balance")
146}
147
148#[allow(unused)]
149struct ListScenario<T: pallet_nomination_pools::Config> {
150 origin1: T::AccountId,
152 creator1: T::AccountId,
153 dest_weight: BalanceOf<T>,
154 origin1_member: Option<T::AccountId>,
155}
156
157impl<T: Config> ListScenario<T> {
158 pub(crate) fn new(
169 origin_weight: BalanceOf<T>,
170 is_increase: bool,
171 ) -> Result<Self, &'static str> {
172 ensure!(!origin_weight.is_zero(), "origin weight must be greater than 0");
173
174 ensure!(
175 pallet_nomination_pools::MaxPools::<T>::get().unwrap_or(0) >= 3,
176 "must allow at least three pools for benchmarks"
177 );
178
179 CurrencyOf::<T>::set_total_issuance(Zero::zero());
181
182 let (pool_creator1, pool_origin1) =
184 create_pool_account::<T>(USER_SEED + 1, origin_weight, Some(Perbill::from_percent(50)));
185
186 T::StakeAdapter::nominate(
187 Pool::from(pool_origin1.clone()),
188 vec![account("random_validator", 0, USER_SEED)],
190 )?;
191
192 let (_, pool_origin2) =
193 create_pool_account::<T>(USER_SEED + 2, origin_weight, Some(Perbill::from_percent(50)));
194
195 T::StakeAdapter::nominate(
196 Pool::from(pool_origin2.clone()),
197 vec![account("random_validator", 0, USER_SEED)].clone(),
198 )?;
199
200 let dest_weight_as_vote = <T as pallet_staking::Config>::VoterList::score_update_worst_case(
202 &pool_origin1,
203 is_increase,
204 );
205
206 let dest_weight: BalanceOf<T> =
207 dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?;
208
209 let (_, pool_dest1) =
211 create_pool_account::<T>(USER_SEED + 3, dest_weight, Some(Perbill::from_percent(50)));
212
213 T::StakeAdapter::nominate(
214 Pool::from(pool_dest1.clone()),
215 vec![account("random_validator", 0, USER_SEED)],
216 )?;
217
218 let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
219 assert_eq!(vote_to_balance::<T>(weight_of(&pool_origin1)).unwrap(), origin_weight);
220 assert_eq!(vote_to_balance::<T>(weight_of(&pool_origin2)).unwrap(), origin_weight);
221 assert_eq!(vote_to_balance::<T>(weight_of(&pool_dest1)).unwrap(), dest_weight);
222
223 Ok(ListScenario {
224 origin1: pool_origin1,
225 creator1: pool_creator1,
226 dest_weight,
227 origin1_member: None,
228 })
229 }
230
231 fn add_joiner(mut self, amount: BalanceOf<T>) -> Self {
232 let amount = MinJoinBond::<T>::get()
233 .max(CurrencyOf::<T>::minimum_balance())
234 .max(amount);
237
238 let joiner: T::AccountId = account("joiner", USER_SEED, 0);
239 self.origin1_member = Some(joiner.clone());
240 CurrencyOf::<T>::set_balance(&joiner, amount * 2u32.into());
241
242 let original_bonded = T::StakeAdapter::active_stake(Pool::from(self.origin1.clone()));
243
244 T::StakeAdapter::unbond(Pool::from(self.origin1.clone()), amount)
247 .expect("the pool was created in `Self::new`.");
248
249 BondedPools::<T>::mutate(&1, |maybe_pool| {
251 maybe_pool.as_mut().map(|pool| pool.points -= amount)
252 });
253
254 Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), amount, 1).unwrap();
255
256 let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
258 assert_eq!(vote_to_balance::<T>(weight_of(&self.origin1)).unwrap(), original_bonded);
259
260 let member = PoolMembers::<T>::get(&joiner).unwrap();
262 assert_eq!(member.points, amount);
263 assert_eq!(member.pool_id, 1);
264
265 self
266 }
267}
268
269#[benchmarks(
270 where
271 T: pallet_staking::Config,
272 pallet_staking::BalanceOf<T>: From<u128>,
273 BalanceOf<T>: Into<u128>,
274)]
275mod benchmarks {
276 use super::*;
277
278 #[benchmark]
279 fn join() {
280 let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
281
282 let scenario = ListScenario::<T>::new(origin_weight, true).unwrap();
284 assert_eq!(
285 T::StakeAdapter::active_stake(Pool::from(scenario.origin1.clone())),
286 origin_weight
287 );
288
289 let max_additional = scenario.dest_weight - origin_weight;
290 let joiner_free = CurrencyOf::<T>::minimum_balance() + max_additional;
291
292 let joiner: T::AccountId = create_funded_user_with_balance::<T>("joiner", 0, joiner_free);
293
294 whitelist_account!(joiner);
295
296 #[extrinsic_call]
297 _(RuntimeOrigin::Signed(joiner.clone()), max_additional, 1);
298
299 assert_eq!(CurrencyOf::<T>::balance(&joiner), joiner_free - max_additional);
300 assert_eq!(
301 T::StakeAdapter::active_stake(Pool::from(scenario.origin1)),
302 scenario.dest_weight
303 );
304 }
305
306 #[benchmark]
307 fn bond_extra_transfer() {
308 let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
309 let scenario = ListScenario::<T>::new(origin_weight, true).unwrap();
310 let extra = scenario.dest_weight - origin_weight;
311
312 #[extrinsic_call]
315 bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::FreeBalance(extra));
316
317 assert!(
318 T::StakeAdapter::active_stake(Pool::from(scenario.origin1)) >= scenario.dest_weight
319 );
320 }
321
322 #[benchmark]
323 fn bond_extra_other() {
324 let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0);
325
326 let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
327 let scenario = ListScenario::<T>::new(origin_weight, true).unwrap();
328 let extra = (scenario.dest_weight - origin_weight).max(CurrencyOf::<T>::minimum_balance());
329
330 let _ = Pools::<T>::set_claim_permission(
333 RuntimeOrigin::Signed(scenario.creator1.clone()).into(),
334 ClaimPermission::PermissionlessAll,
335 );
336
337 let reward_account1 = Pools::<T>::generate_reward_account(1);
339 assert!(extra >= CurrencyOf::<T>::minimum_balance());
340 let _ = CurrencyOf::<T>::mint_into(&reward_account1, extra);
341
342 #[extrinsic_call]
343 _(
344 RuntimeOrigin::Signed(claimer),
345 T::Lookup::unlookup(scenario.creator1.clone()),
346 BondExtra::Rewards,
347 );
348
349 assert!(
351 T::StakeAdapter::active_stake(Pool::from(scenario.origin1)) >=
352 scenario.dest_weight / 2u32.into()
353 );
354 }
355
356 #[benchmark]
357 fn claim_payout() {
358 let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0);
359 let commission = Perbill::from_percent(50);
360 let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
361 let ed = CurrencyOf::<T>::minimum_balance();
362 let (depositor, _pool_account) =
363 create_pool_account::<T>(0, origin_weight, Some(commission));
364 let reward_account = Pools::<T>::generate_reward_account(1);
365
366 CurrencyOf::<T>::set_balance(&reward_account, ed + origin_weight);
368
369 let _ = Pools::<T>::set_claim_permission(
372 RuntimeOrigin::Signed(depositor.clone()).into(),
373 ClaimPermission::PermissionlessAll,
374 );
375
376 assert_eq!(CurrencyOf::<T>::balance(&depositor), origin_weight);
378
379 whitelist_account!(depositor);
380
381 #[extrinsic_call]
382 claim_payout_other(RuntimeOrigin::Signed(claimer), depositor.clone());
383
384 assert_eq!(
385 CurrencyOf::<T>::balance(&depositor),
386 origin_weight + commission * origin_weight
387 );
388 assert_eq!(CurrencyOf::<T>::balance(&reward_account), ed + commission * origin_weight);
389 }
390
391 #[benchmark]
392 fn unbond() {
393 let origin_weight = Pools::<T>::depositor_min_bond() * 200u32.into();
396 let scenario = ListScenario::<T>::new(origin_weight, false).unwrap();
397 let amount = origin_weight - scenario.dest_weight;
398
399 let scenario = scenario.add_joiner(amount);
400 let member_id = scenario.origin1_member.unwrap().clone();
401 let member_id_lookup = T::Lookup::unlookup(member_id.clone());
402 let all_points = PoolMembers::<T>::get(&member_id).unwrap().points;
403 whitelist_account!(member_id);
404
405 #[extrinsic_call]
406 _(RuntimeOrigin::Signed(member_id.clone()), member_id_lookup, all_points);
407
408 let bonded_after = T::StakeAdapter::active_stake(Pool::from(scenario.origin1));
409 assert!(bonded_after <= scenario.dest_weight);
411 let member = PoolMembers::<T>::get(&member_id).unwrap();
412 assert_eq!(
413 member.unbonding_eras.keys().cloned().collect::<Vec<_>>(),
414 vec![0 + T::StakeAdapter::bonding_duration()]
415 );
416 assert_eq!(member.unbonding_eras.values().cloned().collect::<Vec<_>>(), vec![all_points]);
417 }
418
419 #[benchmark]
420 fn pool_withdraw_unbonded(s: Linear<0, MAX_SPANS>) {
421 let min_create_bond = Pools::<T>::depositor_min_bond();
422 let (_depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
423
424 let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
426 let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 2u32.into());
427 Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1).unwrap();
428
429 assert_eq!(
431 T::StakeAdapter::active_stake(Pool::from(pool_account.clone())),
432 min_create_bond + min_join_bond
433 );
434 assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
435
436 Pools::<T>::fully_unbond(RuntimeOrigin::Signed(joiner.clone()).into(), joiner.clone())
438 .unwrap();
439
440 assert_eq!(
442 T::StakeAdapter::active_stake(Pool::from(pool_account.clone())),
443 min_create_bond
444 );
445 assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
446 pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
448
449 pallet_staking::benchmarking::add_slashing_spans::<T>(&pool_account, s);
451 whitelist_account!(pool_account);
452
453 #[extrinsic_call]
454 _(RuntimeOrigin::Signed(pool_account.clone()), 1, s);
455
456 assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
458 assert_eq!(pallet_staking::Ledger::<T>::get(pool_account).unwrap().unlocking.len(), 0);
460 }
461
462 #[benchmark]
463 fn withdraw_unbonded_update(s: Linear<0, MAX_SPANS>) {
464 let min_create_bond = Pools::<T>::depositor_min_bond();
465 let (_depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
466
467 let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
469 let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 2u32.into());
470 let joiner_lookup = T::Lookup::unlookup(joiner.clone());
471 Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1).unwrap();
472
473 assert_eq!(
475 T::StakeAdapter::active_stake(Pool::from(pool_account.clone())),
476 min_create_bond + min_join_bond
477 );
478 assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
479
480 pallet_staking::CurrentEra::<T>::put(0);
482 Pools::<T>::fully_unbond(RuntimeOrigin::Signed(joiner.clone()).into(), joiner.clone())
483 .unwrap();
484
485 assert_eq!(
487 T::StakeAdapter::active_stake(Pool::from(pool_account.clone())),
488 min_create_bond
489 );
490 assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
491
492 pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
494
495 pallet_staking::benchmarking::add_slashing_spans::<T>(&pool_account, s);
496 whitelist_account!(joiner);
497
498 #[extrinsic_call]
499 withdraw_unbonded(RuntimeOrigin::Signed(joiner.clone()), joiner_lookup, s);
500
501 assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond * 2u32.into());
502 assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 0);
504 }
505
506 #[benchmark]
507 fn withdraw_unbonded_kill(s: Linear<0, MAX_SPANS>) {
508 let min_create_bond = Pools::<T>::depositor_min_bond();
509 let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
510 let depositor_lookup = T::Lookup::unlookup(depositor.clone());
511
512 BondedPools::<T>::try_mutate(&1, |maybe_bonded_pool| {
514 maybe_bonded_pool.as_mut().ok_or(()).map(|bonded_pool| {
515 bonded_pool.state = PoolState::Destroying;
516 })
517 })
518 .unwrap();
519
520 pallet_staking::CurrentEra::<T>::put(0);
522 let reward_account = Pools::<T>::generate_reward_account(1);
527 assert!(frame_system::Account::<T>::contains_key(&reward_account));
528 Pools::<T>::fully_unbond(
529 RuntimeOrigin::Signed(depositor.clone()).into(),
530 depositor.clone(),
531 )
532 .unwrap();
533
534 assert_eq!(T::StakeAdapter::active_stake(Pool::from(pool_account.clone())), Zero::zero());
536 assert_eq!(
537 T::StakeAdapter::total_balance(Pool::from(pool_account.clone())),
538 Some(min_create_bond)
539 );
540 assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
541
542 pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
544
545 assert!(pallet_staking::Ledger::<T>::contains_key(&pool_account));
547 assert!(BondedPools::<T>::contains_key(&1));
548 assert!(SubPoolsStorage::<T>::contains_key(&1));
549 assert!(RewardPools::<T>::contains_key(&1));
550 assert!(PoolMembers::<T>::contains_key(&depositor));
551 assert!(frame_system::Account::<T>::contains_key(&reward_account));
552
553 whitelist_account!(depositor);
554
555 #[extrinsic_call]
556 withdraw_unbonded(RuntimeOrigin::Signed(depositor.clone()), depositor_lookup, s);
557
558 assert!(!pallet_staking::Ledger::<T>::contains_key(&pool_account));
560 assert!(!BondedPools::<T>::contains_key(&1));
561 assert!(!SubPoolsStorage::<T>::contains_key(&1));
562 assert!(!RewardPools::<T>::contains_key(&1));
563 assert!(!PoolMembers::<T>::contains_key(&depositor));
564 assert!(!frame_system::Account::<T>::contains_key(&pool_account));
565 assert!(!frame_system::Account::<T>::contains_key(&reward_account));
566
567 assert_eq!(
569 CurrencyOf::<T>::balance(&depositor),
570 min_create_bond * 2u32.into() + CurrencyOf::<T>::minimum_balance()
572 );
573 }
574
575 #[benchmark]
576 fn create() {
577 let min_create_bond = Pools::<T>::depositor_min_bond();
578 let depositor: T::AccountId = account("depositor", USER_SEED, 0);
579 let depositor_lookup = T::Lookup::unlookup(depositor.clone());
580
581 CurrencyOf::<T>::set_balance(
585 &depositor,
586 min_create_bond + CurrencyOf::<T>::minimum_balance() * 2u32.into(),
587 );
588 assert_eq!(RewardPools::<T>::count(), 0);
590 assert_eq!(BondedPools::<T>::count(), 0);
591
592 whitelist_account!(depositor);
593
594 #[extrinsic_call]
595 _(
596 RuntimeOrigin::Signed(depositor.clone()),
597 min_create_bond,
598 depositor_lookup.clone(),
599 depositor_lookup.clone(),
600 depositor_lookup,
601 );
602
603 assert_eq!(RewardPools::<T>::count(), 1);
604 assert_eq!(BondedPools::<T>::count(), 1);
605 let (_, new_pool) = BondedPools::<T>::iter().next().unwrap();
606 assert_eq!(
607 new_pool,
608 BondedPoolInner {
609 commission: Commission::default(),
610 member_counter: 1,
611 points: min_create_bond,
612 roles: PoolRoles {
613 depositor: depositor.clone(),
614 root: Some(depositor.clone()),
615 nominator: Some(depositor.clone()),
616 bouncer: Some(depositor.clone()),
617 },
618 state: PoolState::Open,
619 }
620 );
621 assert_eq!(
622 T::StakeAdapter::active_stake(Pool::from(Pools::<T>::generate_bonded_account(1))),
623 min_create_bond
624 );
625 }
626
627 #[benchmark]
628 fn nominate(n: Linear<1, { MaxNominationsOf::<T>::get() }>) {
629 let min_create_bond = Pools::<T>::depositor_min_bond() * 2u32.into();
631 let (depositor, _pool_account) = create_pool_account::<T>(0, min_create_bond, None);
632
633 let validators: Vec<_> = (0..n).map(|i| account("stash", USER_SEED, i)).collect();
636
637 whitelist_account!(depositor);
638
639 #[extrinsic_call]
640 _(RuntimeOrigin::Signed(depositor.clone()), 1, validators);
641
642 assert_eq!(RewardPools::<T>::count(), 1);
643 assert_eq!(BondedPools::<T>::count(), 1);
644 let (_, new_pool) = BondedPools::<T>::iter().next().unwrap();
645 assert_eq!(
646 new_pool,
647 BondedPoolInner {
648 commission: Commission::default(),
649 member_counter: 1,
650 points: min_create_bond,
651 roles: PoolRoles {
652 depositor: depositor.clone(),
653 root: Some(depositor.clone()),
654 nominator: Some(depositor.clone()),
655 bouncer: Some(depositor.clone()),
656 },
657 state: PoolState::Open,
658 }
659 );
660 assert_eq!(
661 T::StakeAdapter::active_stake(Pool::from(Pools::<T>::generate_bonded_account(1))),
662 min_create_bond
663 );
664 }
665
666 #[benchmark]
667 fn set_state() {
668 let min_create_bond = Pools::<T>::depositor_min_bond();
670 let _ = create_pool_account::<T>(0, min_create_bond, None);
672 BondedPools::<T>::mutate(&1, |maybe_pool| {
673 maybe_pool.as_mut().map(|pool| pool.points = min_create_bond * 10u32.into());
675 });
676
677 let caller = account("caller", 0, USER_SEED);
678 whitelist_account!(caller);
679
680 #[extrinsic_call]
681 _(RuntimeOrigin::Signed(caller), 1, PoolState::Destroying);
682
683 assert_eq!(BondedPools::<T>::get(1).unwrap().state, PoolState::Destroying);
684 }
685
686 #[benchmark]
687 fn set_metadata(
688 n: Linear<1, { <T as pallet_nomination_pools::Config>::MaxMetadataLen::get() }>,
689 ) {
690 let (depositor, _pool_account) =
692 create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
693
694 let metadata: Vec<u8> = (0..n).map(|_| 42).collect();
696
697 whitelist_account!(depositor);
698
699 #[extrinsic_call]
700 _(RuntimeOrigin::Signed(depositor), 1, metadata.clone());
701 assert_eq!(Metadata::<T>::get(&1), metadata);
702 }
703
704 #[benchmark]
705 fn set_configs() {
706 #[extrinsic_call]
707 _(
708 RuntimeOrigin::Root,
709 ConfigOp::Set(BalanceOf::<T>::max_value()),
710 ConfigOp::Set(BalanceOf::<T>::max_value()),
711 ConfigOp::Set(u32::MAX),
712 ConfigOp::Set(u32::MAX),
713 ConfigOp::Set(u32::MAX),
714 ConfigOp::Set(Perbill::max_value()),
715 );
716
717 assert_eq!(MinJoinBond::<T>::get(), BalanceOf::<T>::max_value());
718 assert_eq!(MinCreateBond::<T>::get(), BalanceOf::<T>::max_value());
719 assert_eq!(MaxPools::<T>::get(), Some(u32::MAX));
720 assert_eq!(MaxPoolMembers::<T>::get(), Some(u32::MAX));
721 assert_eq!(MaxPoolMembersPerPool::<T>::get(), Some(u32::MAX));
722 assert_eq!(GlobalMaxCommission::<T>::get(), Some(Perbill::max_value()));
723 }
724
725 #[benchmark]
726 fn update_roles() {
727 let first_id = pallet_nomination_pools::LastPoolId::<T>::get() + 1;
728 let (root, _) =
729 create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
730 let random: T::AccountId =
731 account("but is anything really random in computers..?", 0, USER_SEED);
732
733 #[extrinsic_call]
734 _(
735 RuntimeOrigin::Signed(root.clone()),
736 first_id,
737 ConfigOp::Set(random.clone()),
738 ConfigOp::Set(random.clone()),
739 ConfigOp::Set(random.clone()),
740 );
741 assert_eq!(
742 pallet_nomination_pools::BondedPools::<T>::get(first_id).unwrap().roles,
743 pallet_nomination_pools::PoolRoles {
744 depositor: root,
745 nominator: Some(random.clone()),
746 bouncer: Some(random.clone()),
747 root: Some(random),
748 },
749 )
750 }
751
752 #[benchmark]
753 fn chill() {
754 let (depositor, pool_account) =
756 create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
757
758 let validators: Vec<_> = (0..MaxNominationsOf::<T>::get())
760 .map(|i| account("stash", USER_SEED, i))
761 .collect();
762
763 assert_ok!(T::StakeAdapter::nominate(Pool::from(pool_account.clone()), validators));
764 assert!(T::StakeAdapter::nominations(Pool::from(pool_account.clone())).is_some());
765
766 whitelist_account!(depositor);
767
768 #[extrinsic_call]
769 _(RuntimeOrigin::Signed(depositor.clone()), 1);
770
771 assert!(T::StakeAdapter::nominations(Pool::from(pool_account.clone())).is_none());
772 }
773
774 #[benchmark]
775 fn set_commission() {
776 let (depositor, _pool_account) =
778 create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
779 Pools::<T>::set_commission_max(
781 RuntimeOrigin::Signed(depositor.clone()).into(),
782 1u32.into(),
783 Perbill::from_percent(50),
784 )
785 .unwrap();
786 Pools::<T>::set_commission_change_rate(
788 RuntimeOrigin::Signed(depositor.clone()).into(),
789 1u32.into(),
790 CommissionChangeRate {
791 max_increase: Perbill::from_percent(20),
792 min_delay: 0u32.into(),
793 },
794 )
795 .unwrap();
796 Pools::<T>::set_commission_claim_permission(
798 RuntimeOrigin::Signed(depositor.clone()).into(),
799 1u32.into(),
800 Some(CommissionClaimPermission::Account(depositor.clone())),
801 )
802 .unwrap();
803
804 #[extrinsic_call]
805 _(
806 RuntimeOrigin::Signed(depositor.clone()),
807 1u32.into(),
808 Some((Perbill::from_percent(20), depositor.clone())),
809 );
810
811 assert_eq!(
812 BondedPools::<T>::get(1).unwrap().commission,
813 Commission {
814 current: Some((Perbill::from_percent(20), depositor.clone())),
815 max: Some(Perbill::from_percent(50)),
816 change_rate: Some(CommissionChangeRate {
817 max_increase: Perbill::from_percent(20),
818 min_delay: 0u32.into()
819 }),
820 throttle_from: Some(1u32.into()),
821 claim_permission: Some(CommissionClaimPermission::Account(depositor)),
822 }
823 );
824 }
825
826 #[benchmark]
827 fn set_commission_max() {
828 let (depositor, _pool_account) = create_pool_account::<T>(
830 0,
831 Pools::<T>::depositor_min_bond() * 2u32.into(),
832 Some(Perbill::from_percent(50)),
833 );
834
835 #[extrinsic_call]
836 _(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50));
837
838 assert_eq!(
839 BondedPools::<T>::get(1).unwrap().commission,
840 Commission {
841 current: Some((Perbill::from_percent(50), depositor)),
842 max: Some(Perbill::from_percent(50)),
843 change_rate: None,
844 throttle_from: Some(0u32.into()),
845 claim_permission: None,
846 }
847 );
848 }
849
850 #[benchmark]
851 fn set_commission_change_rate() {
852 let (depositor, _pool_account) =
854 create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
855
856 #[extrinsic_call]
857 _(
858 RuntimeOrigin::Signed(depositor.clone()),
859 1u32.into(),
860 CommissionChangeRate {
861 max_increase: Perbill::from_percent(50),
862 min_delay: 1000u32.into(),
863 },
864 );
865
866 assert_eq!(
867 BondedPools::<T>::get(1).unwrap().commission,
868 Commission {
869 current: None,
870 max: None,
871 change_rate: Some(CommissionChangeRate {
872 max_increase: Perbill::from_percent(50),
873 min_delay: 1000u32.into(),
874 }),
875 throttle_from: Some(1_u32.into()),
876 claim_permission: None,
877 }
878 );
879 }
880
881 #[benchmark]
882 fn set_commission_claim_permission() {
883 let (depositor, _pool_account) =
885 create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
886
887 #[extrinsic_call]
888 _(
889 RuntimeOrigin::Signed(depositor.clone()),
890 1u32.into(),
891 Some(CommissionClaimPermission::Account(depositor.clone())),
892 );
893
894 assert_eq!(
895 BondedPools::<T>::get(1).unwrap().commission,
896 Commission {
897 current: None,
898 max: None,
899 change_rate: None,
900 throttle_from: None,
901 claim_permission: Some(CommissionClaimPermission::Account(depositor)),
902 }
903 );
904 }
905
906 #[benchmark]
907 fn set_claim_permission() {
908 let min_create_bond = Pools::<T>::depositor_min_bond();
910 let (_depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
911
912 let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
914 let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 4u32.into());
915 Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1).unwrap();
916
917 assert_eq!(
919 T::StakeAdapter::active_stake(Pool::from(pool_account.clone())),
920 min_create_bond + min_join_bond
921 );
922
923 #[extrinsic_call]
924 _(RuntimeOrigin::Signed(joiner.clone()), ClaimPermission::Permissioned);
925
926 assert_eq!(ClaimPermissions::<T>::get(joiner), ClaimPermission::Permissioned);
927 }
928
929 #[benchmark]
930 fn claim_commission() {
931 let claimer: T::AccountId = account("claimer_member", USER_SEED + 4, 0);
932 let commission = Perbill::from_percent(50);
933 let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
934 let ed = CurrencyOf::<T>::minimum_balance();
935 let (depositor, _pool_account) =
936 create_pool_account::<T>(0, origin_weight, Some(commission));
937 let reward_account = Pools::<T>::generate_reward_account(1);
938 CurrencyOf::<T>::set_balance(&reward_account, ed + origin_weight);
939
940 let _ = Pools::<T>::claim_payout(RuntimeOrigin::Signed(claimer.clone()).into());
942 let _ = Pools::<T>::set_commission_claim_permission(
944 RuntimeOrigin::Signed(depositor.clone()).into(),
945 1u32.into(),
946 Some(CommissionClaimPermission::Account(claimer)),
947 );
948 whitelist_account!(depositor);
949
950 #[extrinsic_call]
951 _(RuntimeOrigin::Signed(depositor.clone()), 1u32.into());
952
953 assert_eq!(
954 CurrencyOf::<T>::balance(&depositor),
955 origin_weight + commission * origin_weight
956 );
957 assert_eq!(CurrencyOf::<T>::balance(&reward_account), ed + commission * origin_weight);
958 }
959
960 #[benchmark]
961 fn adjust_pool_deposit() {
962 let (depositor, _) =
964 create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
965
966 let _ = Pools::<T>::unfreeze_pool_deposit(&Pools::<T>::generate_reward_account(1));
968
969 assert_eq!(Pools::<T>::check_ed_imbalance().unwrap(), 1);
970
971 whitelist_account!(depositor);
972
973 #[extrinsic_call]
974 _(RuntimeOrigin::Signed(depositor), 1);
975
976 assert_eq!(Pools::<T>::check_ed_imbalance().unwrap(), 0);
977 }
978
979 #[benchmark]
980 fn apply_slash() {
981 let deposit_amount =
983 Pools::<T>::depositor_min_bond() * T::MaxUnbonding::get().into() * 4u32.into();
984 let (depositor, pool_account) = create_pool_account::<T>(0, deposit_amount, None);
985 let depositor_lookup = T::Lookup::unlookup(depositor.clone());
986
987 assert_eq!(PoolMembers::<T>::get(&depositor).unwrap().total_balance(), deposit_amount);
989 assert!(
991 T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) ==
992 Some(deposit_amount),
993 );
994
995 let slash_amount: u128 = deposit_amount.into() / 2;
998
999 pallet_staking::slashing::do_slash::<T>(
1001 &pool_account,
1002 slash_amount.into(),
1003 &mut pallet_staking::BalanceOf::<T>::zero(),
1004 &mut pallet_staking::NegativeImbalanceOf::<T>::zero(),
1005 EraIndex::zero(),
1006 );
1007
1008 assert_eq!(
1010 PoolMembers::<T>::get(&depositor).unwrap().total_balance(),
1011 deposit_amount / 2u32.into()
1012 );
1013 assert!(
1015 T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) ==
1016 Some(deposit_amount),
1017 );
1018
1019 for i in 1..(T::MaxUnbonding::get() + 1) {
1021 pallet_staking::CurrentEra::<T>::put(i);
1022 assert!(Pools::<T>::unbond(
1023 RuntimeOrigin::Signed(depositor.clone()).into(),
1024 depositor_lookup.clone(),
1025 Pools::<T>::depositor_min_bond()
1026 )
1027 .is_ok());
1028 }
1029
1030 pallet_staking::CurrentEra::<T>::put(T::MaxUnbonding::get() + 2);
1031
1032 let slash_reporter =
1033 create_funded_user_with_balance::<T>("slasher", 0, CurrencyOf::<T>::minimum_balance());
1034 whitelist_account!(depositor);
1035
1036 #[block]
1037 {
1038 assert!(Pools::<T>::apply_slash(
1039 RuntimeOrigin::Signed(slash_reporter.clone()).into(),
1040 depositor_lookup.clone(),
1041 )
1042 .is_ok(),);
1043 }
1044
1045 assert_eq!(
1047 PoolMembers::<T>::get(&depositor).unwrap().total_balance(),
1048 deposit_amount / 2u32.into()
1049 );
1050 assert!(
1051 T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) ==
1052 Some(deposit_amount / 2u32.into()),
1053 );
1054 }
1055
1056 #[benchmark]
1057 fn apply_slash_fail() {
1058 let deposit_amount = Pools::<T>::depositor_min_bond() * 10u32.into();
1061 let (_depositor, pool_account) = create_pool_account::<T>(0, deposit_amount, None);
1063
1064 let slash_amount: u128 = deposit_amount.into() / 2;
1066 pallet_staking::slashing::do_slash::<T>(
1067 &pool_account,
1068 slash_amount.into(),
1069 &mut pallet_staking::BalanceOf::<T>::zero(),
1070 &mut pallet_staking::NegativeImbalanceOf::<T>::zero(),
1071 EraIndex::zero(),
1072 );
1073
1074 pallet_staking::CurrentEra::<T>::put(1);
1075
1076 let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
1078 let join_amount = min_join_bond * T::MaxUnbonding::get().into() * 2u32.into();
1079 let joiner = create_funded_user_with_balance::<T>("joiner", 0, join_amount * 2u32.into());
1080 let joiner_lookup = T::Lookup::unlookup(joiner.clone());
1081 assert!(
1082 Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), join_amount, 1).is_ok()
1083 );
1084
1085 for i in 0..T::MaxUnbonding::get() {
1087 pallet_staking::CurrentEra::<T>::put(i + 2); assert!(Pools::<T>::unbond(
1089 RuntimeOrigin::Signed(joiner.clone()).into(),
1090 joiner_lookup.clone(),
1091 min_join_bond
1092 )
1093 .is_ok());
1094 }
1095
1096 pallet_staking::CurrentEra::<T>::put(T::MaxUnbonding::get() + 3);
1097 whitelist_account!(joiner);
1098
1099 #[block]
1102 {
1103 assert!(Pools::<T>::apply_slash(
1104 RuntimeOrigin::Signed(joiner.clone()).into(),
1105 joiner_lookup.clone()
1106 )
1107 .is_err());
1108 }
1109 }
1110
1111 #[benchmark]
1112 fn pool_migrate() {
1113 let deposit_amount = Pools::<T>::depositor_min_bond() * 2u32.into();
1115 let (depositor, pool_account) = create_pool_account::<T>(0, deposit_amount, None);
1116
1117 let _ = migrate_to_transfer_stake::<T>(1);
1119 #[block]
1120 {
1121 assert!(Pools::<T>::migrate_pool_to_delegate_stake(
1122 RuntimeOrigin::Signed(depositor.clone()).into(),
1123 1u32.into(),
1124 )
1125 .is_ok(),);
1126 }
1127 assert_eq!(
1129 T::StakeAdapter::total_balance(Pool::from(pool_account.clone())),
1130 Some(deposit_amount + CurrencyOf::<T>::minimum_balance())
1131 );
1132 }
1133
1134 #[benchmark]
1135 fn migrate_delegation() {
1136 let deposit_amount = Pools::<T>::depositor_min_bond() * 2u32.into();
1138 let (depositor, _pool_account) = create_pool_account::<T>(0, deposit_amount, None);
1139 let depositor_lookup = T::Lookup::unlookup(depositor.clone());
1140
1141 let _ = migrate_to_transfer_stake::<T>(1);
1143
1144 assert!(Pools::<T>::migrate_pool_to_delegate_stake(
1146 RuntimeOrigin::Signed(depositor.clone()).into(),
1147 1u32.into(),
1148 )
1149 .is_ok(),);
1150
1151 assert!(
1153 T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())).is_none()
1154 );
1155 assert_eq!(PoolMembers::<T>::get(&depositor).unwrap().total_balance(), deposit_amount);
1157
1158 whitelist_account!(depositor);
1159
1160 #[block]
1161 {
1162 assert!(Pools::<T>::migrate_delegation(
1163 RuntimeOrigin::Signed(depositor.clone()).into(),
1164 depositor_lookup.clone(),
1165 )
1166 .is_ok(),);
1167 }
1168 assert!(
1170 T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) ==
1171 Some(deposit_amount),
1172 );
1173 assert_eq!(PoolMembers::<T>::get(&depositor).unwrap().total_balance(), deposit_amount);
1174 }
1175
1176 impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime);
1177}