From 82cc7edb23aa1e2e8b51d89760b8ea74d0dd0772 Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Mon, 29 Mar 2021 11:24:50 -0400 Subject: [PATCH] stake: fix collator fee to 20% (#306) * done * bump spec * fix types --- moonbeam-types-bundle/index.ts | 2 - pallets/parachain-staking/src/lib.rs | 40 +++++++++---- pallets/parachain-staking/src/mock.rs | 4 +- pallets/parachain-staking/src/tests.rs | 82 +++++++++++--------------- runtime/src/lib.rs | 6 +- 5 files changed, 65 insertions(+), 69 deletions(-) diff --git a/moonbeam-types-bundle/index.ts b/moonbeam-types-bundle/index.ts index dab3b42d7aadf..0bba0eeaeec72 100644 --- a/moonbeam-types-bundle/index.ts +++ b/moonbeam-types-bundle/index.ts @@ -200,14 +200,12 @@ export const moonbeamDefinitions = { OrderedSet: "Vec", Collator: { id: "AccountId", - fee: "Perbill", bond: "Balance", nominators: "Vec", total: "Balance", state: "CollatorStatus", }, CollatorSnapshot: { - fee: "Perbill", bond: "Balance", nominators: "Vec", total: "Balance", diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index 09841286dec2c..f8a02286652c1 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -131,7 +131,6 @@ pub mod pallet { #[derive(Default, Encode, Decode, RuntimeDebug)] /// Snapshot of collator state at the start of the round for which they are selected pub struct CollatorSnapshot { - pub fee: Perbill, pub bond: Balance, pub nominators: Vec>, pub total: Balance, @@ -141,7 +140,6 @@ pub mod pallet { /// Global collator state with commission fee, bonded stake, and nominations pub struct Collator { pub id: AccountId, - pub fee: Perbill, pub bond: Balance, pub nominators: OrderedSet>, pub total: Balance, @@ -153,11 +151,10 @@ pub mod pallet { B: AtLeast32BitUnsigned + Ord + Copy + sp_std::ops::AddAssign + sp_std::ops::SubAssign, > Collator { - pub fn new(id: A, fee: Perbill, bond: B) -> Self { + pub fn new(id: A, bond: B) -> Self { let total = bond; Collator { id, - fee, bond, nominators: OrderedSet::new(), total, @@ -216,7 +213,6 @@ pub mod pallet { impl From> for CollatorSnapshot { fn from(other: Collator) -> CollatorSnapshot { CollatorSnapshot { - fee: other.fee, bond: other.bond, nominators: other.nominators.0, total: other.total, @@ -389,8 +385,8 @@ pub mod pallet { type MaxNominatorsPerCollator: Get; /// Maximum collators per nominator type MaxCollatorsPerNominator: Get; - /// Maximum fee for any collator - type MaxFee: Get; + /// Commission due to collators, set at genesis + type DefaultCollatorCommission: Get; /// Minimum stake required for any account to be in `SelectedCandidates` for the round type MinCollatorStk: Get>; /// Minimum stake required for any account to be a collator candidate @@ -408,7 +404,6 @@ pub mod pallet { CandidateDNE, NominatorExists, CandidateExists, - FeeOverMax, ValBondBelowMin, NomBondBelowMin, NominationBelowMin, @@ -462,6 +457,8 @@ pub mod pallet { StakeExpectationsSet(BalanceOf, BalanceOf, BalanceOf), /// Set total selected candidates to this value [old, new] TotalSelectedSet(u32, u32), + /// Set collator commission to this value [old, new] + CollatorCommissionSet(Perbill, Perbill), /// Set blocks per round [current_round, first_block, old, new] BlocksPerRoundSet(RoundIndex, T::BlockNumber, u32, u32), } @@ -493,6 +490,11 @@ pub mod pallet { } } + #[pallet::storage] + #[pallet::getter(fn collator_commission)] + /// Commission percent taken off of rewards for all collators + type CollatorCommission = StorageValue<_, Perbill, ValueQuery>; + #[pallet::storage] #[pallet::getter(fn total_selected)] /// The total candidates selected every round @@ -622,11 +624,12 @@ pub mod pallet { } else { >::join_candidates( T::Origin::from(Some(actor.clone()).into()), - Perbill::zero(), // default fee for collators registered at genesis is 0% balance, ) }; } + // Set collator commission to default config + >::put(T::DefaultCollatorCommission::get()); // Set total selected candidates to minimum config >::put(T::MinSelectedCandidates::get()); // Choose top TotalSelected collator candidates @@ -700,6 +703,18 @@ pub mod pallet { Ok(().into()) } #[pallet::weight(0)] + /// Set the commission for all collators + pub fn set_collator_commission( + origin: OriginFor, + pct: Perbill, + ) -> DispatchResultWithPostInfo { + frame_system::ensure_root(origin)?; + let old = >::get(); + >::put(pct); + Self::deposit_event(Event::CollatorCommissionSet(old, pct)); + Ok(().into()) + } + #[pallet::weight(0)] /// Set blocks per round /// - if called with `new` less than length of current round, will transition immediately /// in the next block @@ -720,13 +735,11 @@ pub mod pallet { #[pallet::weight(0)] pub fn join_candidates( origin: OriginFor, - fee: Perbill, bond: BalanceOf, ) -> DispatchResultWithPostInfo { let acc = ensure_signed(origin)?; ensure!(!Self::is_candidate(&acc), Error::::CandidateExists); ensure!(!Self::is_nominator(&acc), Error::::NominatorExists); - ensure!(fee <= T::MaxFee::get(), Error::::FeeOverMax); ensure!( bond >= T::MinCollatorCandidateStk::get(), Error::::ValBondBelowMin @@ -740,7 +753,7 @@ pub mod pallet { Error::::CandidateExists ); T::Currency::reserve(&acc, bond)?; - let candidate = Collator::new(acc.clone(), fee, bond); + let candidate = Collator::new(acc.clone(), bond); let new_total = >::get() + bond; >::put(new_total); >::insert(&acc, candidate); @@ -1147,6 +1160,7 @@ pub mod pallet { } }; let duration = T::BondDuration::get(); + let collator_fee = >::get(); if next > duration { let round_to_payout = next - duration; let total = >::get(round_to_payout); @@ -1166,7 +1180,7 @@ pub mod pallet { } else { // pay collator first; commission + due_portion let val_pct = Perbill::from_rational(state.bond, state.total); - let commission = state.fee * amt_due; + let commission = collator_fee * amt_due; let val_due = if commission > T::Currency::minimum_balance() { amt_due -= commission; (val_pct * amt_due) + commission diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index c94b12d772fcf..3604795dfc783 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -100,7 +100,7 @@ parameter_types! { pub const MinSelectedCandidates: u32 = 5; pub const MaxNominatorsPerCollator: u32 = 4; pub const MaxCollatorsPerNominator: u32 = 4; - pub const MaxFee: Perbill = Perbill::from_percent(50); + pub const DefaultCollatorCommission: Perbill = Perbill::from_percent(20); pub const MinCollatorStk: u128 = 10; pub const MinNominatorStk: u128 = 5; pub const MinNomination: u128 = 3; @@ -114,7 +114,7 @@ impl Config for Test { type MinSelectedCandidates = MinSelectedCandidates; type MaxNominatorsPerCollator = MaxNominatorsPerCollator; type MaxCollatorsPerNominator = MaxCollatorsPerNominator; - type MaxFee = MaxFee; + type DefaultCollatorCommission = DefaultCollatorCommission; type MinCollatorStk = MinCollatorStk; type MinCollatorCandidateStk = MinCollatorStk; type MinNominatorStk = MinNominatorStk; diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index 9c5d2a58d91b9..26f6837a24d14 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -22,7 +22,7 @@ use crate::mock::{ }; use crate::{CollatorStatus, Error, Event}; use frame_support::{assert_noop, assert_ok}; -use sp_runtime::{traits::Zero, DispatchError, Perbill}; +use sp_runtime::{traits::Zero, DispatchError}; #[test] fn geneses() { @@ -131,35 +131,27 @@ fn online_offline_works() { fn join_collator_candidates() { two_collators_four_nominators().execute_with(|| { assert_noop!( - Stake::join_candidates(Origin::signed(1), Perbill::from_percent(2), 11u128,), + Stake::join_candidates(Origin::signed(1), 11u128,), Error::::CandidateExists ); assert_noop!( - Stake::join_candidates(Origin::signed(3), Perbill::from_percent(2), 11u128,), + Stake::join_candidates(Origin::signed(3), 11u128,), Error::::NominatorExists ); assert_noop!( - Stake::join_candidates(Origin::signed(7), Perbill::from_percent(2), 9u128,), + Stake::join_candidates(Origin::signed(7), 9u128,), Error::::ValBondBelowMin ); assert_noop!( - Stake::join_candidates(Origin::signed(8), Perbill::from_percent(2), 10u128,), + Stake::join_candidates(Origin::signed(8), 10u128,), DispatchError::Module { index: 1, error: 3, message: Some("InsufficientBalance") } ); - assert_noop!( - Stake::join_candidates(Origin::signed(7), Perbill::from_percent(51), 10u128,), - Error::::FeeOverMax - ); assert!(System::events().is_empty()); - assert_ok!(Stake::join_candidates( - Origin::signed(7), - Perbill::from_percent(3), - 10u128, - )); + assert_ok!(Stake::join_candidates(Origin::signed(7), 10u128,)); assert_eq!( last_event(), MetaEvent::stake(Event::JoinedCollatorCandidates(7, 10u128, 1110u128)) @@ -225,11 +217,7 @@ fn collator_selection_chooses_top_candidates() { MetaEvent::stake(Event::CollatorScheduledExit(2, 6, 4)) ); roll_to(21); - assert_ok!(Stake::join_candidates( - Origin::signed(6), - Perbill::from_percent(2), - 69u128 - )); + assert_ok!(Stake::join_candidates(Origin::signed(6), 69u128)); assert_eq!( last_event(), MetaEvent::stake(Event::JoinedCollatorCandidates(6, 69u128, 469u128)) @@ -448,11 +436,7 @@ fn collator_commission() { Event::NewRound(5, 2, 1, 40), ]; assert_eq!(events(), expected); - assert_ok!(Stake::join_candidates( - Origin::signed(4), - Perbill::from_percent(20), - 20u128 - )); + assert_ok!(Stake::join_candidates(Origin::signed(4), 20u128)); assert_eq!( last_event(), MetaEvent::stake(Event::JoinedCollatorCandidates(4, 20u128, 60u128)) @@ -783,10 +767,10 @@ fn payouts_follow_nomination_changes() { Event::CollatorChosen(3, 3, 20), Event::CollatorChosen(3, 5, 10), Event::NewRound(10, 3, 5, 140), - Event::Rewarded(1, 20), - Event::Rewarded(6, 10), - Event::Rewarded(7, 10), - Event::Rewarded(10, 10), + Event::Rewarded(1, 26), + Event::Rewarded(6, 8), + Event::Rewarded(7, 8), + Event::Rewarded(10, 8), Event::CollatorChosen(4, 1, 50), Event::CollatorChosen(4, 2, 40), Event::CollatorChosen(4, 4, 20), @@ -810,10 +794,10 @@ fn payouts_follow_nomination_changes() { let mut new2 = vec![ Event::NominatorLeftCollator(6, 1, 10, 40), Event::NominatorLeft(6, 10), - Event::Rewarded(1, 21), - Event::Rewarded(6, 10), - Event::Rewarded(7, 10), - Event::Rewarded(10, 10), + Event::Rewarded(1, 27), + Event::Rewarded(6, 8), + Event::Rewarded(7, 8), + Event::Rewarded(10, 8), Event::CollatorChosen(5, 2, 40), Event::CollatorChosen(5, 1, 40), Event::CollatorChosen(5, 4, 20), @@ -828,10 +812,10 @@ fn payouts_follow_nomination_changes() { roll_to(26); // keep paying 6 let mut new3 = vec![ - Event::Rewarded(1, 22), - Event::Rewarded(6, 11), - Event::Rewarded(7, 11), - Event::Rewarded(10, 11), + Event::Rewarded(1, 29), + Event::Rewarded(6, 9), + Event::Rewarded(7, 9), + Event::Rewarded(10, 9), Event::CollatorChosen(6, 2, 40), Event::CollatorChosen(6, 1, 40), Event::CollatorChosen(6, 4, 20), @@ -845,9 +829,9 @@ fn payouts_follow_nomination_changes() { roll_to(31); // no more paying 6 let mut new4 = vec![ - Event::Rewarded(1, 29), - Event::Rewarded(7, 14), - Event::Rewarded(10, 14), + Event::Rewarded(1, 35), + Event::Rewarded(7, 11), + Event::Rewarded(10, 11), Event::CollatorChosen(7, 2, 40), Event::CollatorChosen(7, 1, 40), Event::CollatorChosen(7, 4, 20), @@ -863,9 +847,9 @@ fn payouts_follow_nomination_changes() { // new nomination is not rewarded yet let mut new5 = vec![ Event::Nomination(8, 10, 1, 50), - Event::Rewarded(1, 30), - Event::Rewarded(7, 15), - Event::Rewarded(10, 15), + Event::Rewarded(1, 36), + Event::Rewarded(7, 12), + Event::Rewarded(10, 12), Event::CollatorChosen(8, 1, 50), Event::CollatorChosen(8, 2, 40), Event::CollatorChosen(8, 4, 20), @@ -879,9 +863,9 @@ fn payouts_follow_nomination_changes() { roll_to(41); // new nomination is still not rewarded yet let mut new6 = vec![ - Event::Rewarded(1, 32), - Event::Rewarded(7, 16), - Event::Rewarded(10, 16), + Event::Rewarded(1, 38), + Event::Rewarded(7, 13), + Event::Rewarded(10, 13), Event::CollatorChosen(9, 1, 50), Event::CollatorChosen(9, 2, 40), Event::CollatorChosen(9, 4, 20), @@ -894,10 +878,10 @@ fn payouts_follow_nomination_changes() { roll_to(46); // new nomination is rewarded for first time, 2 rounds after joining (`BondDuration` = 2) let mut new7 = vec![ - Event::Rewarded(1, 27), - Event::Rewarded(7, 13), - Event::Rewarded(8, 13), - Event::Rewarded(10, 13), + Event::Rewarded(1, 35), + Event::Rewarded(7, 11), + Event::Rewarded(8, 11), + Event::Rewarded(10, 11), Event::CollatorChosen(10, 1, 50), Event::CollatorChosen(10, 2, 40), Event::CollatorChosen(10, 4, 20), diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index bad80f1ac1c19..bc1859ebf6d5f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -393,8 +393,8 @@ parameter_types! { pub const MaxNominatorsPerCollator: u32 = 10; /// Maximum 25 collators per nominator pub const MaxCollatorsPerNominator: u32 = 25; - /// The maximum percent a collator can take off the top of its rewards is 50% - pub const MaxFee: Perbill = Perbill::from_percent(50); + /// The fixed percent a collator takes off the top of due rewards is 20% + pub const DefaultCollatorCommission: Perbill = Perbill::from_percent(20); /// Minimum stake required to be reserved to be a collator is 1_000 pub const MinCollatorStk: u128 = 1_000 * GLMR; /// Minimum stake required to be reserved to be a nominator is 5 @@ -409,7 +409,7 @@ impl parachain_staking::Config for Runtime { type MinSelectedCandidates = MinSelectedCandidates; type MaxNominatorsPerCollator = MaxNominatorsPerCollator; type MaxCollatorsPerNominator = MaxCollatorsPerNominator; - type MaxFee = MaxFee; + type DefaultCollatorCommission = DefaultCollatorCommission; type MinCollatorStk = MinCollatorStk; type MinCollatorCandidateStk = MinCollatorStk; type MinNomination = MinNominatorStk;