Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit ae83a67

Browse files
Ross Bulatgpestanakianenigmabkchr
authored
Nomination Pool Commission (#13128)
* + nomination pool commission * fmt * use register_update() * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana <[email protected]> * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana <[email protected]> * fmt * amend comments * + test for set_commission * fix * Update frame/nomination-pools/fuzzer/src/call.rs Co-authored-by: Kian Paimani <[email protected]> * rm comment * use PalletError * some feedback item amendments * update weights * revert PalletError stuff * ".git/.scripts/commands/fmt/fmt.sh" * make pool_events_since_last_call more modular * fmt * fix call indexes + test * add payout teste * add event to max_commisson updating current * begin refactor * some debugging * update * more tests * rewardpol not working * commission refactor * pending rewards returns commission * fmt * add claim_commission call * + claim_commission * fix benchmarks * weight 0 for now * + claim_commission benchmark * fmt * apply commission to benchmarks * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools * ".git/.scripts/commands/fmt/fmt.sh" * clippy * + pending * add RewardPool.total_rewards_acounted * fixes * println * more logs * Fix plus cleanups * fix assert * tidy up * tests work + tidy up * rm unused * clippy fix * persist reward_pool update * claim_commission_works tests * . * some test formatting * add high level docs * add calls * docs * rename * rename * docs * rename * fmt * use matches! * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana <[email protected]> * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana <[email protected]> * Update frame/nomination-pools/src/tests.rs Co-authored-by: Gonçalo Pestana <[email protected]> * comment * Update frame/nomination-pools/src/lib.rs Co-authored-by: Gonçalo Pestana <[email protected]> * . * weights order * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools * use from_parts * comment * ".git/.scripts/commands/fmt/fmt.sh" * revert clippy suggestions on old migrations * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nomination_pools * add InitialGlobalMaxCommission * fix migration * reward counter comments & explanations * format * add commission implementation note * fmt * revert InitialGlobalMaxCommission * global max commission migration generic * text * 100% commission no payout test * add commission_accumulates_on_multiple_rewards * non-zero fuzzer GlobalMaxCommission * add last_recorded_total_payouts_needs_commission * commission event fix + claim commission test --------- Co-authored-by: Gonçalo Pestana <[email protected]> Co-authored-by: Kian Paimani <[email protected]> Co-authored-by: command-bot <> Co-authored-by: Bastian Köcher <[email protected]>
1 parent e44038a commit ae83a67

File tree

9 files changed

+2730
-462
lines changed

9 files changed

+2730
-462
lines changed

frame/nomination-pools/benchmarking/src/lib.rs

Lines changed: 135 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,14 @@ use frame_support::{assert_ok, ensure, traits::Get};
3131
use frame_system::RawOrigin as RuntimeOrigin;
3232
use pallet_nomination_pools::{
3333
BalanceOf, BondExtra, BondedPoolInner, BondedPools, ClaimPermission, ClaimPermissions,
34-
ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond,
35-
MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage,
34+
Commission, CommissionChangeRate, ConfigOp, GlobalMaxCommission, MaxPoolMembers,
35+
MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools,
36+
PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage,
37+
};
38+
use sp_runtime::{
39+
traits::{Bounded, StaticLookup, Zero},
40+
Perbill,
3641
};
37-
use sp_runtime::traits::{Bounded, StaticLookup, Zero};
3842
use sp_staking::{EraIndex, StakingInterface};
3943
// `frame_benchmarking::benchmarks!` macro needs this
4044
use pallet_nomination_pools::Call;
@@ -69,6 +73,7 @@ fn create_funded_user_with_balance<T: pallet_nomination_pools::Config>(
6973
fn create_pool_account<T: pallet_nomination_pools::Config>(
7074
n: u32,
7175
balance: BalanceOf<T>,
76+
commission: Option<Perbill>,
7277
) -> (T::AccountId, T::AccountId) {
7378
let ed = CurrencyOf::<T>::minimum_balance();
7479
let pool_creator: T::AccountId =
@@ -84,6 +89,16 @@ fn create_pool_account<T: pallet_nomination_pools::Config>(
8489
)
8590
.unwrap();
8691

92+
if let Some(c) = commission {
93+
let pool_id = pallet_nomination_pools::LastPoolId::<T>::get();
94+
Pools::<T>::set_commission(
95+
RuntimeOrigin::Signed(pool_creator.clone()).into(),
96+
pool_id,
97+
Some((c, pool_creator.clone())),
98+
)
99+
.expect("pool just created, commission can be set by root; qed");
100+
}
101+
87102
let pool_account = pallet_nomination_pools::BondedPools::<T>::iter()
88103
.find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator)
89104
.map(|(pool_id, _)| Pools::<T>::create_bonded_account(pool_id))
@@ -134,14 +149,18 @@ impl<T: Config> ListScenario<T> {
134149
sp_std::mem::forget(i);
135150

136151
// Create accounts with the origin weight
137-
let (pool_creator1, pool_origin1) = create_pool_account::<T>(USER_SEED + 1, origin_weight);
152+
let (pool_creator1, pool_origin1) =
153+
create_pool_account::<T>(USER_SEED + 1, origin_weight, Some(Perbill::from_percent(50)));
154+
138155
T::Staking::nominate(
139156
&pool_origin1,
140157
// NOTE: these don't really need to be validators.
141158
vec![account("random_validator", 0, USER_SEED)],
142159
)?;
143160

144-
let (_, pool_origin2) = create_pool_account::<T>(USER_SEED + 2, origin_weight);
161+
let (_, pool_origin2) =
162+
create_pool_account::<T>(USER_SEED + 2, origin_weight, Some(Perbill::from_percent(50)));
163+
145164
T::Staking::nominate(
146165
&pool_origin2,
147166
vec![account("random_validator", 0, USER_SEED)].clone(),
@@ -157,7 +176,9 @@ impl<T: Config> ListScenario<T> {
157176
dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?;
158177

159178
// Create an account with the worst case destination weight
160-
let (_, pool_dest1) = create_pool_account::<T>(USER_SEED + 3, dest_weight);
179+
let (_, pool_dest1) =
180+
create_pool_account::<T>(USER_SEED + 3, dest_weight, Some(Perbill::from_percent(50)));
181+
161182
T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?;
162183

163184
let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
@@ -269,18 +290,19 @@ frame_benchmarking::benchmarks! {
269290

270291
}: _(RuntimeOrigin::Signed(claimer), T::Lookup::unlookup(scenario.creator1.clone()), BondExtra::Rewards)
271292
verify {
293+
// commission of 50% deducted here.
272294
assert!(
273295
T::Staking::active_stake(&scenario.origin1).unwrap() >=
274-
scenario.dest_weight
296+
scenario.dest_weight / 2u32.into()
275297
);
276298
}
277299

278300
claim_payout {
279301
let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0);
280-
302+
let commission = Perbill::from_percent(50);
281303
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
282304
let ed = CurrencyOf::<T>::minimum_balance();
283-
let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight);
305+
let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight, Some(commission));
284306
let reward_account = Pools::<T>::create_reward_account(1);
285307

286308
// Send funds to the reward account of the pool
@@ -301,11 +323,11 @@ frame_benchmarking::benchmarks! {
301323
verify {
302324
assert_eq!(
303325
CurrencyOf::<T>::free_balance(&depositor),
304-
origin_weight * 2u32.into()
326+
origin_weight + commission * origin_weight
305327
);
306328
assert_eq!(
307329
CurrencyOf::<T>::free_balance(&reward_account),
308-
ed + Zero::zero()
330+
ed + commission * origin_weight
309331
);
310332
}
311333

@@ -345,7 +367,7 @@ frame_benchmarking::benchmarks! {
345367
let s in 0 .. MAX_SPANS;
346368

347369
let min_create_bond = Pools::<T>::depositor_min_bond();
348-
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
370+
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
349371

350372
// Add a new member
351373
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
@@ -387,7 +409,7 @@ frame_benchmarking::benchmarks! {
387409
let s in 0 .. MAX_SPANS;
388410

389411
let min_create_bond = Pools::<T>::depositor_min_bond();
390-
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
412+
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
391413

392414
// Add a new member
393415
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
@@ -433,7 +455,7 @@ frame_benchmarking::benchmarks! {
433455
let s in 0 .. MAX_SPANS;
434456

435457
let min_create_bond = Pools::<T>::depositor_min_bond();
436-
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
458+
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
437459
let depositor_lookup = T::Lookup::unlookup(depositor.clone());
438460

439461
// We set the pool to the destroying state so the depositor can leave
@@ -523,15 +545,16 @@ frame_benchmarking::benchmarks! {
523545
assert_eq!(
524546
new_pool,
525547
BondedPoolInner {
526-
points: min_create_bond,
527-
state: PoolState::Open,
548+
commission: Commission::default(),
528549
member_counter: 1,
550+
points: min_create_bond,
529551
roles: PoolRoles {
530552
depositor: depositor.clone(),
531553
root: Some(depositor.clone()),
532554
nominator: Some(depositor.clone()),
533555
bouncer: Some(depositor.clone()),
534556
},
557+
state: PoolState::Open,
535558
}
536559
);
537560
assert_eq!(
@@ -545,7 +568,7 @@ frame_benchmarking::benchmarks! {
545568

546569
// Create a pool
547570
let min_create_bond = Pools::<T>::depositor_min_bond() * 2u32.into();
548-
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
571+
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
549572

550573
// Create some accounts to nominate. For the sake of benchmarking they don't need to be
551574
// actual validators
@@ -562,15 +585,16 @@ frame_benchmarking::benchmarks! {
562585
assert_eq!(
563586
new_pool,
564587
BondedPoolInner {
565-
points: min_create_bond,
566-
state: PoolState::Open,
588+
commission: Commission::default(),
567589
member_counter: 1,
590+
points: min_create_bond,
568591
roles: PoolRoles {
569592
depositor: depositor.clone(),
570593
root: Some(depositor.clone()),
571594
nominator: Some(depositor.clone()),
572595
bouncer: Some(depositor.clone()),
573-
}
596+
},
597+
state: PoolState::Open,
574598
}
575599
);
576600
assert_eq!(
@@ -582,7 +606,7 @@ frame_benchmarking::benchmarks! {
582606
set_state {
583607
// Create a pool
584608
let min_create_bond = Pools::<T>::depositor_min_bond();
585-
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
609+
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
586610
BondedPools::<T>::mutate(&1, |maybe_pool| {
587611
// Force the pool into an invalid state
588612
maybe_pool.as_mut().map(|mut pool| pool.points = min_create_bond * 10u32.into());
@@ -599,7 +623,7 @@ frame_benchmarking::benchmarks! {
599623
let n in 1 .. <T as pallet_nomination_pools::Config>::MaxMetadataLen::get();
600624

601625
// Create a pool
602-
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into());
626+
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
603627

604628
// Create metadata of the max possible size
605629
let metadata: Vec<u8> = (0..n).map(|_| 42).collect();
@@ -617,18 +641,20 @@ frame_benchmarking::benchmarks! {
617641
ConfigOp::Set(BalanceOf::<T>::max_value()),
618642
ConfigOp::Set(u32::MAX),
619643
ConfigOp::Set(u32::MAX),
620-
ConfigOp::Set(u32::MAX)
644+
ConfigOp::Set(u32::MAX),
645+
ConfigOp::Set(Perbill::max_value())
621646
) verify {
622647
assert_eq!(MinJoinBond::<T>::get(), BalanceOf::<T>::max_value());
623648
assert_eq!(MinCreateBond::<T>::get(), BalanceOf::<T>::max_value());
624649
assert_eq!(MaxPools::<T>::get(), Some(u32::MAX));
625650
assert_eq!(MaxPoolMembers::<T>::get(), Some(u32::MAX));
626651
assert_eq!(MaxPoolMembersPerPool::<T>::get(), Some(u32::MAX));
652+
assert_eq!(GlobalMaxCommission::<T>::get(), Some(Perbill::max_value()));
627653
}
628654

629655
update_roles {
630656
let first_id = pallet_nomination_pools::LastPoolId::<T>::get() + 1;
631-
let (root, _) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into());
657+
let (root, _) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
632658
let random: T::AccountId = account("but is anything really random in computers..?", 0, USER_SEED);
633659
}:_(
634660
RuntimeOrigin::Signed(root.clone()),
@@ -650,7 +676,7 @@ frame_benchmarking::benchmarks! {
650676

651677
chill {
652678
// Create a pool
653-
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into());
679+
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
654680

655681
// Nominate with the pool.
656682
let validators: Vec<_> = (0..T::MaxNominations::get())
@@ -666,10 +692,68 @@ frame_benchmarking::benchmarks! {
666692
assert!(T::Staking::nominations(Pools::<T>::create_bonded_account(1)).is_none());
667693
}
668694

695+
set_commission {
696+
// Create a pool - do not set a commission yet.
697+
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
698+
// set a max commission
699+
Pools::<T>::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap();
700+
// set a change rate
701+
Pools::<T>::set_commission_change_rate(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate {
702+
max_increase: Perbill::from_percent(20),
703+
min_delay: 0u32.into(),
704+
}).unwrap();
705+
706+
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some((Perbill::from_percent(20), depositor.clone())))
707+
verify {
708+
assert_eq!(BondedPools::<T>::get(1).unwrap().commission, Commission {
709+
current: Some((Perbill::from_percent(20), depositor)),
710+
max: Some(Perbill::from_percent(50)),
711+
change_rate: Some(CommissionChangeRate {
712+
max_increase: Perbill::from_percent(20),
713+
min_delay: 0u32.into()
714+
}),
715+
throttle_from: Some(1u32.into()),
716+
});
717+
}
718+
719+
set_commission_max {
720+
// Create a pool, setting a commission that will update when max commission is set.
721+
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), Some(Perbill::from_percent(50)));
722+
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50))
723+
verify {
724+
assert_eq!(
725+
BondedPools::<T>::get(1).unwrap().commission, Commission {
726+
current: Some((Perbill::from_percent(50), depositor)),
727+
max: Some(Perbill::from_percent(50)),
728+
change_rate: None,
729+
throttle_from: Some(0u32.into()),
730+
});
731+
}
732+
733+
set_commission_change_rate {
734+
// Create a pool
735+
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
736+
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionChangeRate {
737+
max_increase: Perbill::from_percent(50),
738+
min_delay: 1000u32.into(),
739+
})
740+
verify {
741+
assert_eq!(
742+
BondedPools::<T>::get(1).unwrap().commission, Commission {
743+
current: None,
744+
max: None,
745+
change_rate: Some(CommissionChangeRate {
746+
max_increase: Perbill::from_percent(50),
747+
min_delay: 1000u32.into(),
748+
}),
749+
throttle_from: Some(1_u32.into()),
750+
});
751+
}
752+
669753
set_claim_permission {
670754
// Create a pool
671755
let min_create_bond = Pools::<T>::depositor_min_bond();
672-
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond);
756+
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
673757

674758
// Join pool
675759
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
@@ -688,6 +772,31 @@ frame_benchmarking::benchmarks! {
688772
assert_eq!(ClaimPermissions::<T>::get(joiner), ClaimPermission::PermissionlessAll);
689773
}
690774

775+
claim_commission {
776+
let claimer: T::AccountId = account("claimer_member", USER_SEED + 4, 0);
777+
let commission = Perbill::from_percent(50);
778+
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
779+
let ed = CurrencyOf::<T>::minimum_balance();
780+
let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight, Some(commission));
781+
let reward_account = Pools::<T>::create_reward_account(1);
782+
CurrencyOf::<T>::make_free_balance_be(&reward_account, ed + origin_weight);
783+
784+
// member claims a payout to make some commission available.
785+
let _ = Pools::<T>::claim_payout(RuntimeOrigin::Signed(claimer).into());
786+
787+
whitelist_account!(depositor);
788+
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into())
789+
verify {
790+
assert_eq!(
791+
CurrencyOf::<T>::free_balance(&depositor),
792+
origin_weight + commission * origin_weight
793+
);
794+
assert_eq!(
795+
CurrencyOf::<T>::free_balance(&reward_account),
796+
ed + commission * origin_weight
797+
);
798+
}
799+
691800
impl_benchmark_test_suite!(
692801
Pallet,
693802
crate::mock::new_test_ext(),

frame/nomination-pools/benchmarking/src/mock.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use frame_election_provider_support::VoteWeight;
2020
use frame_support::{pallet_prelude::*, parameter_types, traits::ConstU64, PalletId};
2121
use sp_runtime::{
2222
traits::{Convert, IdentityLookup},
23-
FixedU128,
23+
FixedU128, Perbill,
2424
};
2525

2626
type AccountId = u128;
@@ -195,6 +195,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
195195
max_pools: Some(3),
196196
max_members_per_pool: Some(3),
197197
max_members: Some(3 * 3),
198+
global_max_commission: Some(Perbill::from_percent(50)),
198199
}
199200
.assimilate_storage(&mut storage);
200201
sp_io::TestExternalities::from(storage)

frame/nomination-pools/fuzzer/src/call.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ use pallet_nomination_pools::{
3333
mock::*,
3434
pallet as pools,
3535
pallet::{BondedPools, Call as PoolsCall, Event as PoolsEvents, PoolMembers},
36-
BondExtra, BondedPool, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools,
37-
MinCreateBond, MinJoinBond, PoolId,
36+
BondExtra, BondedPool, GlobalMaxCommission, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool,
37+
MaxPools, MinCreateBond, MinJoinBond, PoolId,
3838
};
3939
use rand::{seq::SliceRandom, Rng};
40-
use sp_runtime::{assert_eq_error_rate, Perquintill};
40+
use sp_runtime::{assert_eq_error_rate, Perbill, Perquintill};
4141

4242
const ERA: BlockNumber = 1000;
4343
const MAX_ED_MULTIPLE: Balance = 10_000;
@@ -224,6 +224,7 @@ fn main() {
224224
MaxPoolMembers::<T>::set(Some(10_000));
225225
MaxPoolMembersPerPool::<T>::set(Some(1000));
226226
MaxPools::<T>::set(Some(1_000));
227+
GlobalMaxCommission::<T>::set(Some(Perbill::from_percent(25)));
227228

228229
MinCreateBond::<T>::set(10 * ExistentialDeposit::get());
229230
MinJoinBond::<T>::set(5 * ExistentialDeposit::get());

0 commit comments

Comments
 (0)