diff --git a/parachain/pallets/parachain-staking/src/delegation_requests.rs b/parachain/pallets/parachain-staking/src/delegation_requests.rs index f03868b477..6a675631cc 100644 --- a/parachain/pallets/parachain-staking/src/delegation_requests.rs +++ b/parachain/pallets/parachain-staking/src/delegation_requests.rs @@ -22,21 +22,23 @@ use crate::{ pallet::{ - BalanceOf, CandidateInfo, Config, DelegationScheduledRequests, DelegatorState, Error, - Event, Pallet, Round, RoundIndex, Total, + AutoCompoundingDelegations, BalanceOf, BottomDelegations, CandidateInfo, CandidatePool, + Config, DelegationScheduledRequests, DelegatorState, Error, Event, Pallet, Round, + RoundIndex, TopDelegations, Total, }, weights::WeightInfo, - AutoCompoundDelegations, Delegator, OnAllDelegationRemoved, + AutoCompoundDelegations, Bond, Delegator, OnAllDelegationRemoved, }; use frame_support::{ dispatch::DispatchResultWithPostInfo, ensure, + pallet_prelude::DispatchResult, traits::{Get, ReservableCurrency}, }; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; -use sp_runtime::traits::Saturating; +use sp_runtime::traits::{Saturating, Zero}; use sp_std::{vec, vec::Vec}; /// An action that can be performed upon a delegation @@ -84,6 +86,115 @@ impl From> for CancelledScheduledRequest { } impl Pallet { + /// Schedules a [CollatorStatus::Leaving] for the candidate + pub(crate) fn candidate_schedule_revoke(collator: T::AccountId) -> DispatchResultWithPostInfo { + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + let (now, when) = state.schedule_leave::()?; + let mut candidates = >::get(); + if candidates.remove(&Bond::from_owner(collator.clone())) { + >::put(candidates); + } + >::insert(&collator, state); + Self::deposit_event(Event::CandidateScheduledExit { + exit_allowed_round: now, + candidate: collator, + scheduled_exit: when, + }); + Ok(().into()) + } + + /// Executes a [CollatorStatus::Leaving] for the candidate + pub(crate) fn candidate_execute_schedule_revoke( + candidate: T::AccountId, + ) -> DispatchResultWithPostInfo { + let state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; + state.can_leave::()?; + let return_stake = |bond: Bond>| -> DispatchResult { + T::Currency::unreserve(&bond.owner, bond.amount); + // remove delegation from delegator state + let mut delegator = DelegatorState::::get(&bond.owner).expect( + "Collator state and delegator state are consistent. + Collator state has a record of this delegation. Therefore, + Delegator state also has a record. qed.", + ); + + if let Some(remaining) = delegator.rm_delegation(&candidate) { + Self::delegation_remove_request_with_state(&candidate, &bond.owner, &mut delegator); + >::remove_auto_compound(&candidate, &bond.owner); + + if remaining.is_zero() { + // we do not remove the scheduled delegation requests from other collators + // since it is assumed that they were removed incrementally before only the + // last delegation was left. + >::remove(&bond.owner); + let _ = T::OnAllDelegationRemoved::on_all_delegation_removed(&bond.owner); + } else { + >::insert(&bond.owner, delegator); + } + } + Ok(()) + }; + // total backing stake is at least the candidate self bond + let mut total_backing = state.bond; + // return all top delegations + let top_delegations = + >::take(&candidate).expect("CandidateInfo existence checked"); + for bond in top_delegations.delegations { + return_stake(bond)?; + } + total_backing = total_backing.saturating_add(top_delegations.total); + // return all bottom delegations + let bottom_delegations = + >::take(&candidate).expect("CandidateInfo existence checked"); + for bond in bottom_delegations.delegations { + return_stake(bond)?; + } + total_backing = total_backing.saturating_add(bottom_delegations.total); + // return stake to collator + T::Currency::unreserve(&candidate, state.bond); + >::remove(&candidate); + >::remove(&candidate); + >::remove(&candidate); + >::remove(&candidate); + >::remove(&candidate); + let new_total_staked = >::get().saturating_sub(total_backing); + >::put(new_total_staked); + Self::deposit_event(Event::CandidateLeft { + ex_candidate: candidate, + unlocked_amount: total_backing, + new_total_amt_locked: new_total_staked, + }); + let actual_weight = + Some(T::WeightInfo::execute_leave_candidates(state.delegation_count as u32)); + Ok(actual_weight.into()) + } + + /// Schedules a [CandidateBondLessRequest] for the candidate + pub(crate) fn candidate_schedule_bond_decrease( + collator: T::AccountId, + less: BalanceOf, + ) -> DispatchResultWithPostInfo { + let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; + let when = state.schedule_bond_less::(less)?; + >::insert(&collator, state); + Self::deposit_event(Event::CandidateBondLessRequested { + candidate: collator, + amount_to_decrease: less, + execute_round: when, + }); + Ok(().into()) + } + + /// Executes a [CandidateBondLessRequest] for the candidate + pub(crate) fn candidate_execute_bond_decrease( + candidate: T::AccountId, + ) -> DispatchResultWithPostInfo { + let mut state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; + state.execute_bond_less::(candidate.clone())?; + >::insert(&candidate, state); + Ok(().into()) + } + /// Schedules a [DelegationAction::Revoke] for the delegator, towards a given collator. pub(crate) fn delegation_schedule_revoke( collator: T::AccountId, @@ -144,7 +255,7 @@ impl Pallet { ensure!(decrease_amount <= max_subtracted_amount, >::DelegatorBondBelowMin); let now = >::get().current; - let when = now.saturating_add(T::RevokeDelegationDelay::get()); + let when = now.saturating_add(T::DelegationBondLessDelay::get()); scheduled_requests.push(ScheduledRequest { delegator: delegator.clone(), action: DelegationAction::Decrease(decrease_amount), diff --git a/parachain/pallets/parachain-staking/src/lib.rs b/parachain/pallets/parachain-staking/src/lib.rs index 3e3f0cbe07..c8c49d3bcc 100644 --- a/parachain/pallets/parachain-staking/src/lib.rs +++ b/parachain/pallets/parachain-staking/src/lib.rs @@ -75,6 +75,12 @@ pub mod set; #[cfg(test)] mod tests; +// Dedicated test for unbond delay = 0 +#[cfg(test)] +mod mock_zero_delay; +#[cfg(test)] +mod tests_zero_delay; + pub use inflation::{InflationInfo, Range}; pub use weights::WeightInfo; @@ -978,21 +984,13 @@ pub mod pallet { /// removed from the candidate pool to prevent selection as a collator. pub fn schedule_leave_candidates(origin: OriginFor) -> DispatchResultWithPostInfo { let collator = ensure_signed(origin)?; - let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; - let (now, when) = state.schedule_leave::()?; - let mut candidates = >::get(); - if candidates.remove(&Bond::from_owner(collator.clone())) { - >::put(candidates); + let _ = Self::candidate_schedule_revoke(collator.clone())?; + if T::LeaveCandidatesDelay::get() == 0u32 { + Self::candidate_execute_schedule_revoke(collator) + } else { + Ok(().into()) } - >::insert(&collator, state); - Self::deposit_event(Event::CandidateScheduledExit { - exit_allowed_round: now, - candidate: collator, - scheduled_exit: when, - }); - Ok(().into()) } - #[pallet::call_index(11)] #[pallet::weight( < T as Config >::WeightInfo::execute_leave_candidates( @@ -1006,70 +1004,7 @@ pub mod pallet { candidate: T::AccountId, ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; - let state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; - state.can_leave::()?; - let return_stake = |bond: Bond>| -> DispatchResult { - T::Currency::unreserve(&bond.owner, bond.amount); - // remove delegation from delegator state - let mut delegator = DelegatorState::::get(&bond.owner).expect( - "Collator state and delegator state are consistent. - Collator state has a record of this delegation. Therefore, - Delegator state also has a record. qed.", - ); - - if let Some(remaining) = delegator.rm_delegation(&candidate) { - Self::delegation_remove_request_with_state( - &candidate, - &bond.owner, - &mut delegator, - ); - >::remove_auto_compound(&candidate, &bond.owner); - - if remaining.is_zero() { - // we do not remove the scheduled delegation requests from other collators - // since it is assumed that they were removed incrementally before only the - // last delegation was left. - >::remove(&bond.owner); - let _ = T::OnAllDelegationRemoved::on_all_delegation_removed(&bond.owner); - } else { - >::insert(&bond.owner, delegator); - } - } - Ok(()) - }; - // total backing stake is at least the candidate self bond - let mut total_backing = state.bond; - // return all top delegations - let top_delegations = - >::take(&candidate).expect("CandidateInfo existence checked"); - for bond in top_delegations.delegations { - return_stake(bond)?; - } - total_backing = total_backing.saturating_add(top_delegations.total); - // return all bottom delegations - let bottom_delegations = - >::take(&candidate).expect("CandidateInfo existence checked"); - for bond in bottom_delegations.delegations { - return_stake(bond)?; - } - total_backing = total_backing.saturating_add(bottom_delegations.total); - // return stake to collator - T::Currency::unreserve(&candidate, state.bond); - >::remove(&candidate); - >::remove(&candidate); - >::remove(&candidate); - >::remove(&candidate); - >::remove(&candidate); - let new_total_staked = >::get().saturating_sub(total_backing); - >::put(new_total_staked); - Self::deposit_event(Event::CandidateLeft { - ex_candidate: candidate, - unlocked_amount: total_backing, - new_total_amt_locked: new_total_staked, - }); - let actual_weight = - Some(T::WeightInfo::execute_leave_candidates(state.delegation_count as u32)); - Ok(actual_weight.into()) + Self::candidate_execute_schedule_revoke(candidate) } #[pallet::call_index(12)] #[pallet::weight(< T as Config >::WeightInfo::cancel_leave_candidates(< CandidatePool < T >>::get().0.len() as u32))] @@ -1151,15 +1086,12 @@ pub mod pallet { less: BalanceOf, ) -> DispatchResultWithPostInfo { let collator = ensure_signed(origin)?; - let mut state = >::get(&collator).ok_or(Error::::CandidateDNE)?; - let when = state.schedule_bond_less::(less)?; - >::insert(&collator, state); - Self::deposit_event(Event::CandidateBondLessRequested { - candidate: collator, - amount_to_decrease: less, - execute_round: when, - }); - Ok(().into()) + let _ = Self::candidate_schedule_bond_decrease(collator.clone(), less)?; + if T::CandidateBondLessDelay::get() == 0u32 { + Self::candidate_execute_bond_decrease(collator) + } else { + Ok(().into()) + } } #[pallet::call_index(17)] #[pallet::weight(< T as Config >::WeightInfo::execute_candidate_bond_less())] @@ -1169,10 +1101,7 @@ pub mod pallet { candidate: T::AccountId, ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; // we may want to reward this if caller != candidate - let mut state = >::get(&candidate).ok_or(Error::::CandidateDNE)?; - state.execute_bond_less::(candidate.clone())?; - >::insert(&candidate, state); - Ok(().into()) + Self::candidate_execute_bond_decrease(candidate) } #[pallet::call_index(18)] #[pallet::weight(< T as Config >::WeightInfo::cancel_candidate_bond_less())] @@ -1242,7 +1171,12 @@ pub mod pallet { /// Success forbids future delegation requests until the request is invoked or cancelled. pub fn schedule_leave_delegators(origin: OriginFor) -> DispatchResultWithPostInfo { let delegator = ensure_signed(origin)?; - Self::delegator_schedule_revoke_all(delegator) + let _ = Self::delegator_schedule_revoke_all(delegator.clone())?; + if T::LeaveDelegatorsDelay::get() == 0u32 { + Self::delegator_execute_scheduled_revoke_all(delegator) + } else { + Ok(().into()) + } } #[pallet::call_index(22)] #[pallet::weight(< T as Config >::WeightInfo::execute_leave_delegators( @@ -1275,7 +1209,12 @@ pub mod pallet { collator: T::AccountId, ) -> DispatchResultWithPostInfo { let delegator = ensure_signed(origin)?; - Self::delegation_schedule_revoke(collator, delegator) + let _ = Self::delegation_schedule_revoke(collator.clone(), delegator.clone())?; + if T::RevokeDelegationDelay::get() == 0u32 { + Self::delegation_execute_scheduled_request(collator, delegator) + } else { + Ok(().into()) + } } #[pallet::call_index(25)] @@ -1311,7 +1250,16 @@ pub mod pallet { less: BalanceOf, ) -> DispatchResultWithPostInfo { let delegator = ensure_signed(origin)?; - Self::delegation_schedule_bond_decrease(candidate, delegator, less) + let _ = Self::delegation_schedule_bond_decrease( + candidate.clone(), + delegator.clone(), + less, + )?; + if T::DelegationBondLessDelay::get() == 0u32 { + Self::delegation_execute_scheduled_request(candidate, delegator) + } else { + Ok(().into()) + } } #[pallet::call_index(27)] diff --git a/parachain/pallets/parachain-staking/src/mock_zero_delay.rs b/parachain/pallets/parachain-staking/src/mock_zero_delay.rs new file mode 100644 index 0000000000..0b90a01a5c --- /dev/null +++ b/parachain/pallets/parachain-staking/src/mock_zero_delay.rs @@ -0,0 +1,192 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +//! Inspired by: +//! - Moonbeam `pallet_parachain_staking` + +use crate as pallet_parachain_staking; +use crate::{Config, InflationInfo, Range}; +use frame_support::{construct_runtime, derive_impl, parameter_types}; +use sp_runtime::{BuildStorage, Perbill, Percent}; + +pub type AccountId = u64; +pub type Balance = u128; + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + ParachainStaking: pallet_parachain_staking, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = frame_system::mocking::MockBlock; + type AccountData = pallet_balances::AccountData; +} + +parameter_types! { + pub const ExistentialDeposit: Balance = 1; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type Balance = Balance; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} + +parameter_types! { + pub const MinBlocksPerRound: u32 = 3; + pub const DefaultBlocksPerRound: u32 = 5; + pub const LeaveCandidatesDelay: u32 = 0; + pub const CandidateBondLessDelay: u32 = 0; + pub const LeaveDelegatorsDelay: u32 = 0; + pub const RevokeDelegationDelay: u32 = 0; + pub const DelegationBondLessDelay: u32 = 0; + pub const RewardPaymentDelay: u32 = 2; + pub const MinSelectedCandidates: u32 = 5; + pub const MaxTopDelegationsPerCandidate: u32 = 4; + pub const MaxBottomDelegationsPerCandidate: u32 = 4; + pub const MaxDelegationsPerDelegator: u32 = 4; + pub const DefaultCollatorCommission: Perbill = Perbill::from_percent(20); + pub const DefaultParachainBondReservePercent: Percent = Percent::from_percent(30); + pub const MinCollatorStk: u128 = 10; + pub const MinDelegatorStk: u128 = 5; + pub const MinDelegation: u128 = 3; +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type MonetaryGovernanceOrigin = frame_system::EnsureRoot; + type MinBlocksPerRound = MinBlocksPerRound; + type DefaultBlocksPerRound = DefaultBlocksPerRound; + type LeaveCandidatesDelay = LeaveCandidatesDelay; + type CandidateBondLessDelay = CandidateBondLessDelay; + type LeaveDelegatorsDelay = LeaveDelegatorsDelay; + type RevokeDelegationDelay = RevokeDelegationDelay; + type DelegationBondLessDelay = DelegationBondLessDelay; + type RewardPaymentDelay = RewardPaymentDelay; + type MinSelectedCandidates = MinSelectedCandidates; + type MaxTopDelegationsPerCandidate = MaxTopDelegationsPerCandidate; + type MaxBottomDelegationsPerCandidate = MaxBottomDelegationsPerCandidate; + type MaxDelegationsPerDelegator = MaxDelegationsPerDelegator; + type DefaultCollatorCommission = DefaultCollatorCommission; + type DefaultParachainBondReservePercent = DefaultParachainBondReservePercent; + type MinCollatorStk = MinCollatorStk; + type MinCandidateStk = MinCollatorStk; + type MinDelegatorStk = MinDelegatorStk; + type MinDelegation = MinDelegation; + type OnCollatorPayout = (); + type OnNewRound = (); + type WeightInfo = (); + type IssuanceAdapter = (); + type OnAllDelegationRemoved = (); +} + +impl pallet_parachain_staking::OnAllDelegationRemoved for () { + fn on_all_delegation_removed( + _delegator: &::AccountId, + ) -> Result<(), &str> { + Ok(()) + } +} + +pub(crate) struct ExtBuilder { + // endowed accounts with balances + balances: Vec<(AccountId, Balance)>, + // [collator, amount] + collators: Vec<(AccountId, Balance)>, + // [delegator, collator, delegation_amount] + delegations: Vec<(AccountId, AccountId, Balance, Percent)>, + // inflation config + inflation: InflationInfo, +} + +impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder { + balances: vec![], + delegations: vec![], + collators: vec![], + inflation: InflationInfo { + expect: Range { min: 700, ideal: 700, max: 700 }, + // not used + annual: Range { + min: Perbill::from_percent(50), + ideal: Perbill::from_percent(50), + max: Perbill::from_percent(50), + }, + // unrealistically high parameterization, only for testing + round: Range { + min: Perbill::from_percent(5), + ideal: Perbill::from_percent(5), + max: Perbill::from_percent(5), + }, + }, + } + } +} + +impl ExtBuilder { + pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub(crate) fn with_candidates(mut self, collators: Vec<(AccountId, Balance)>) -> Self { + self.collators = collators; + self + } + + pub(crate) fn with_delegations( + mut self, + delegations: Vec<(AccountId, AccountId, Balance)>, + ) -> Self { + self.delegations = + delegations.into_iter().map(|d| (d.0, d.1, d.2, Percent::zero())).collect(); + self + } + + #[allow(dead_code)] + pub(crate) fn with_inflation(mut self, inflation: InflationInfo) -> Self { + self.inflation = inflation; + self + } + + pub(crate) fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { balances: self.balances } + .assimilate_storage(&mut t) + .expect("Pallet balances storage can be assimilated"); + pallet_parachain_staking::GenesisConfig:: { + candidates: self.collators, + delegations: self.delegations, + inflation_config: self.inflation, + } + .assimilate_storage(&mut t) + .expect("Parachain Staking's storage can be assimilated"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} diff --git a/parachain/pallets/parachain-staking/src/tests_zero_delay.rs b/parachain/pallets/parachain-staking/src/tests_zero_delay.rs new file mode 100644 index 0000000000..09478a748f --- /dev/null +++ b/parachain/pallets/parachain-staking/src/tests_zero_delay.rs @@ -0,0 +1,107 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . +use crate::{ + mock_zero_delay::{ExtBuilder, ParachainStaking, RuntimeOrigin, Test}, + Error, +}; +use frame_support::{assert_noop, assert_ok}; + +#[test] +fn batch_unstake_and_leave_delegators_works_if_zero_delay() { + ExtBuilder::default() + .with_balances(vec![(1, 130), (2, 110)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + // Execute immediately + assert_ok!(ParachainStaking::schedule_leave_delegators(RuntimeOrigin::signed(2))); + assert_noop!( + ParachainStaking::execute_leave_delegators(RuntimeOrigin::signed(2), 2), + Error::::DelegatorDNE + ); + }); +} + +#[test] +fn batch_unstake_and_leave_candidates_works_if_zero_delay() { + ExtBuilder::default() + .with_balances(vec![(1, 110)]) + .with_candidates(vec![(1, 10)]) + .build() + .execute_with(|| { + // Execute immediately + assert_ok!(ParachainStaking::schedule_leave_candidates(RuntimeOrigin::signed(1))); + assert_noop!( + ParachainStaking::execute_leave_candidates(RuntimeOrigin::signed(1), 1), + Error::::CandidateDNE + ); + }); +} + +#[test] +fn batch_unstake_and_delegator_bond_less_works_if_zero_delay() { + ExtBuilder::default() + .with_balances(vec![(1, 130), (2, 110)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + // Execute immediately + assert_ok!(ParachainStaking::schedule_delegator_bond_less( + RuntimeOrigin::signed(2), + 1, + 1 + )); + assert_noop!( + ParachainStaking::execute_delegation_request(RuntimeOrigin::signed(2), 2, 1), + Error::::PendingDelegationRequestDNE + ); + }); +} + +#[test] +fn batch_unstake_and_revoke_delegation_works_if_zero_delay() { + ExtBuilder::default() + .with_balances(vec![(1, 130), (2, 110)]) + .with_candidates(vec![(1, 30)]) + .with_delegations(vec![(2, 1, 10)]) + .build() + .execute_with(|| { + // Execute immediately + assert_ok!(ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 1)); + assert_noop!( + ParachainStaking::execute_delegation_request(RuntimeOrigin::signed(2), 2, 1), + Error::::DelegatorDNE + ); + }); +} + +#[test] +fn batch_unstake_and_candidate_bond_less_works_if_zero_delay() { + ExtBuilder::default() + .with_balances(vec![(1, 110)]) + .with_candidates(vec![(1, 20)]) + .build() + .execute_with(|| { + // Execute immediately + assert_ok!(ParachainStaking::schedule_candidate_bond_less(RuntimeOrigin::signed(1), 1)); + assert_noop!( + ParachainStaking::execute_candidate_bond_less(RuntimeOrigin::signed(1), 1), + Error::::PendingCandidateRequestsDNE + ); + }); +} diff --git a/parachain/runtime/litentry/src/lib.rs b/parachain/runtime/litentry/src/lib.rs index 94d830f6bd..54a6690f60 100644 --- a/parachain/runtime/litentry/src/lib.rs +++ b/parachain/runtime/litentry/src/lib.rs @@ -992,11 +992,11 @@ impl pallet_parachain_staking::Config for Runtime { /// Rounds before the candidate bond increase/decrease can be executed type CandidateBondLessDelay = ConstU32<{ prod_or_fast!(8, 1) }>; /// Rounds before the delegator exit can be executed - type LeaveDelegatorsDelay = ConstU32<{ prod_or_fast!(8, 1) }>; + type LeaveDelegatorsDelay = ConstU32<{ prod_or_fast!(0, 0) }>; /// Rounds before the delegator revocation can be executed - type RevokeDelegationDelay = ConstU32<{ prod_or_fast!(8, 1) }>; + type RevokeDelegationDelay = ConstU32<{ prod_or_fast!(0, 0) }>; /// Rounds before the delegator bond increase/decrease can be executed - type DelegationBondLessDelay = ConstU32<{ prod_or_fast!(8, 1) }>; + type DelegationBondLessDelay = ConstU32<{ prod_or_fast!(0, 0) }>; /// Rounds before the reward is paid type RewardPaymentDelay = ConstU32<2>; /// Minimum collators selected per round, default at genesis and minimum forever after diff --git a/parachain/runtime/paseo/src/lib.rs b/parachain/runtime/paseo/src/lib.rs index b476273f2d..3de10a5f47 100644 --- a/parachain/runtime/paseo/src/lib.rs +++ b/parachain/runtime/paseo/src/lib.rs @@ -1038,11 +1038,11 @@ impl pallet_parachain_staking::Config for Runtime { /// Rounds before the candidate bond increase/decrease can be executed type CandidateBondLessDelay = ConstU32<{ prod_or_fast!(8, 1) }>; /// Rounds before the delegator exit can be executed - type LeaveDelegatorsDelay = ConstU32<{ prod_or_fast!(8, 1) }>; + type LeaveDelegatorsDelay = ConstU32<{ prod_or_fast!(0, 0) }>; /// Rounds before the delegator revocation can be executed - type RevokeDelegationDelay = ConstU32<{ prod_or_fast!(8, 1) }>; + type RevokeDelegationDelay = ConstU32<{ prod_or_fast!(0, 0) }>; /// Rounds before the delegator bond increase/decrease can be executed - type DelegationBondLessDelay = ConstU32<{ prod_or_fast!(8, 1) }>; + type DelegationBondLessDelay = ConstU32<{ prod_or_fast!(0, 0) }>; /// Rounds before the reward is paid type RewardPaymentDelay = ConstU32<2>; /// Minimum collators selected per round, default at genesis and minimum forever after diff --git a/parachain/ts-tests/integration-tests/precompile-contract.test.ts b/parachain/ts-tests/integration-tests/precompile-contract.test.ts index 5c6c16ba55..a85bf8b4f1 100644 --- a/parachain/ts-tests/integration-tests/precompile-contract.test.ts +++ b/parachain/ts-tests/integration-tests/precompile-contract.test.ts @@ -293,74 +293,10 @@ describeLitentry('Test Parachain Precompile Contract', ``, (context) => { precompileStakingContractAddress, 'scheduleDelegatorBondLess' ); - expect(await isPendingRequest()).to.be.true; - // cancelDelegationRequest(collator) - const cancelDelegationRequest = precompileStakingContract.interface.encodeFunctionData( - 'cancelDelegationRequest', - [collatorPublicKey] - ); - - expect(await isPendingRequest()).to.be.true; - await executeTransaction(cancelDelegationRequest, precompileStakingContractAddress, 'cancelDelegationRequest'); + // Zero delay impl will make execution immediately expect(await isPendingRequest()).to.be.false; - // only makes sense when parachain is compiled with `fast-runtime` feature, otherwise we'll - // never make it within reasonable time - if (config.parachain_fast_runtime === 'true') { - // testing bond less + execution - await executeTransaction( - scheduleDelegatorBondLess, - precompileStakingContractAddress, - 'scheduleDelegatorBondLess again to test execution' - ); - expect(await isPendingRequest()).to.be.true; - - // executeDelegationRequest(delegator, collator); - const executeDelegationRequest = precompileStakingContract.interface.encodeFunctionData( - 'executeDelegationRequest', - [evmAccountRaw.publicKey, collatorPublicKey] - ); - await executeTransaction( - executeDelegationRequest, - precompileStakingContractAddress, - 'executeDelegationRequest' - ); - const { data: balanceAfterBondLess } = await context.api.query.system.account(evmAccountRaw.mappedAddress); - expect(balanceAfterBondLess.reserved.toBigInt()).to.eq( - balanceAfterBondMore.reserved.toBigInt() - toBigInt(5) - ); - - // testing revoke delegation + execute - // scheduleRevokeDelegation(collator); - const scheduleRevokeDelegation = precompileStakingContract.interface.encodeFunctionData( - 'scheduleRevokeDelegation', - [collatorPublicKey] - ); - await executeTransaction( - scheduleRevokeDelegation, - precompileStakingContractAddress, - 'scheduleRevokeDelegation' - ); - - await executeTransaction( - executeDelegationRequest, - precompileStakingContractAddress, - 'executeDelegationRequest' - ); - const { data: balanceAfterRevoke } = await context.api.query.system.account(evmAccountRaw.mappedAddress); - expect(balanceAfterRevoke.reserved.toBigInt()).to.eq(toBigInt(0)); - - // delegate(collator, amount); - const delegate = precompileStakingContract.interface.encodeFunctionData('delegate', [ - collatorPublicKey, - ethers.utils.parseUnits('57', 18).toString(), - ]); - await executeTransaction(delegate, precompileStakingContractAddress, 'delegate'); - const { data: balanceAfterDelegate } = await context.api.query.system.account(evmAccountRaw.mappedAddress); - expect(balanceAfterDelegate.reserved.toBigInt()).to.eq(toBigInt(57)); - } - console.timeEnd('Test precompile staking contract'); });