diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index 1284732f4..95838a473 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -1374,6 +1374,31 @@ pub mod pallet { let delegator = ensure_signed(origin)?; Self::delegation_cancel_request(candidate, delegator) } + + #[pallet::call_index(27)] + #[pallet::weight(::WeightInfo::schedule_revoke_delegation())] + /// Root only. Request to revoke an existing delegation. If successful, the delegation is scheduled + /// to be allowed to be revoked via the `execute_delegation_request` extrinsic. + pub fn force_schedule_revoke_delegation( + origin: OriginFor, + delegator: T::AccountId, + collator: T::AccountId, + ) -> DispatchResultWithPostInfo { + frame_system::ensure_root(origin)?; + Self::delegation_schedule_revoke(collator, delegator) + } + + #[pallet::call_index(28)] + #[pallet::weight(::WeightInfo::execute_delegator_bond_less())] + /// Root only. Execute pending request to change an existing delegation + pub fn force_execute_delegation_request( + origin: OriginFor, + delegator: T::AccountId, + candidate: T::AccountId, + ) -> DispatchResultWithPostInfo { + frame_system::ensure_root(origin)?; + Self::delegation_execute_scheduled_request(candidate, delegator) + } } impl Pallet { diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index 235e1f5b7..e1bc1312d 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -34,7 +34,10 @@ use crate::{ DelegatorStatus, Error, Event, Range, DELEGATOR_LOCK_ID, }; use frame_support::{assert_noop, assert_ok}; -use sp_runtime::{traits::Zero, DispatchError, ModuleError, Perbill, Percent}; +use sp_runtime::{ + traits::{BadOrigin, Zero}, + DispatchError, ModuleError, Perbill, Percent, +}; // ~~ ROOT ~~ @@ -2852,6 +2855,72 @@ fn cannot_cancel_leave_delegators_if_single_delegation_revoke_manually_cancelled // SCHEDULE REVOKE DELEGATION +#[test] +fn force_revoke_delegation_event_emits_correctly() { + ExtBuilder::default() + .with_balances(vec![(1, 30), (2, 20), (3, 30)]) + .with_candidates(vec![(1, 30), (3, 30)]) + .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) + .build() + .execute_with(|| { + // non root signer cannot trigger this extrinsic + assert_noop!( + ParachainStaking::force_schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 2, // delegator + 1 // collator + ), + BadOrigin + ); + // invalid delegatot will fail as well + assert_noop!( + ParachainStaking::force_schedule_revoke_delegation( + RuntimeOrigin::root(), + 5, // invalid delegator + 1 // collator + ), + Error::::DelegatorDNE + ); + // only root can trigger this extrinsic + assert_ok!(ParachainStaking::force_schedule_revoke_delegation( + RuntimeOrigin::root(), + 2, // delegator + 1 // collator + )); + assert_last_event!(MetaEvent::ParachainStaking( + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + } + )); + roll_to(10); + // non root signer cannot trigger this extrinsic + assert_noop!( + ParachainStaking::force_execute_delegation_request(RuntimeOrigin::signed(2), 2, 1), + BadOrigin + ); + // invalid delegatot will fail as well + assert_noop!( + ParachainStaking::force_execute_delegation_request(RuntimeOrigin::root(), 5, 1), + Error::::DelegatorDNE + ); + // only root can trigger this extrinsic + assert_ok!(ParachainStaking::force_execute_delegation_request( + RuntimeOrigin::root(), + 2, + 1 + )); + assert_event_emitted!(Event::DelegatorLeftCandidate { + delegator: 2, + candidate: 1, + unstaked_amount: 10, + total_candidate_staked: 30 + }); + }); +} + #[test] fn revoke_delegation_event_emits_correctly() { ExtBuilder::default()