diff --git a/pallets/parachain-staking/src/benchmarks.rs b/pallets/parachain-staking/src/benchmarks.rs index ad78d2f8770..e51d9f79804 100644 --- a/pallets/parachain-staking/src/benchmarks.rs +++ b/pallets/parachain-staking/src/benchmarks.rs @@ -795,325 +795,6 @@ benchmarks! { assert!(Pallet::::is_delegator(&caller)); } - schedule_leave_delegators_worst { - // We assume that all candidates have full delegations with scheduled requests - let x in 1..T::MaxDelegationsPerDelegator::get(); - - let mut seed = Seed::new(); - - let delegator = create_account::( - "delegator", - seed.take(), - AccountBalance::Value( - T::MinDelegation::get() * T::MaxDelegationsPerDelegator::get().into() * 3000u32.into() - ), - AccountAction::None, - )?; - - for c in 0..x { - let collator = create_account::( - "collator", - seed.take(), - AccountBalance::MinCandidateStake, - AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: c + 1 }, - )?; - - let mut decreasing_balance = >::new( - T::MinDelegation::get() * 2000u32.into(), - 1u32.into(), - ); - let mut col_del_count = 0u32; - for i in 0..T::MaxTopDelegationsPerCandidate::get() - 1 { - let del = create_account::( - "delegator", - seed.take(), - AccountBalance::Value(decreasing_balance.take()), - AccountAction::Delegate{ - collator: collator.clone(), - amount: Amount::All, - auto_compound: Percent::from_percent(100), - collator_delegation_count: col_del_count, - collator_auto_compound_delegation_count: col_del_count, - }, - )?; - col_del_count += 1; - - Pallet::::schedule_delegator_bond_less( - RawOrigin::Signed(del).into(), - collator.clone(), - 5u32.into(), - )?; - } - - let last_top_delegator_bond = decreasing_balance.take(); - >::delegate_with_auto_compound( - RawOrigin::Signed(delegator.clone()).into(), - collator.clone(), - last_top_delegator_bond, - Percent::from_percent(100), - col_del_count, - col_del_count, - c, - )?; - col_del_count += 1; - - assert_eq!( - >::get(&collator).map(|d| d.delegations.len()).unwrap_or_default(), - 0, - ); - - for i in 0..T::MaxBottomDelegationsPerCandidate::get() { - let del = create_account::( - "delegator", - seed.take(), - AccountBalance::Value(decreasing_balance.take()), - AccountAction::Delegate{ - collator: collator.clone(), - amount: Amount::All, - auto_compound: Percent::from_percent(100), - collator_delegation_count: col_del_count, - collator_auto_compound_delegation_count: col_del_count, - }, - )?; - col_del_count += 1; - - Pallet::::schedule_revoke_delegation( - RawOrigin::Signed(del).into(), - collator.clone(), - )?; - } - - assert_eq!( - >::get(&collator) - .map(|bd| bd.delegations.len() as u32) - .unwrap_or_default(), - T::MaxBottomDelegationsPerCandidate::get(), - ); - } - }: { - >::schedule_leave_delegators(RawOrigin::Signed(delegator.clone()).into())?; - } verify { - assert!(Pallet::::delegator_state(&delegator).expect("must exist").is_active()); - } - - cancel_leave_delegators_worst { - // We assume that all candidates have full delegations with scheduled requests - let x in 1..T::MaxDelegationsPerDelegator::get(); - - let mut seed = Seed::new(); - - let delegator = create_account::( - "delegator", - seed.take(), - AccountBalance::Value( - T::MinDelegation::get() * T::MaxDelegationsPerDelegator::get().into() * 3000u32.into() - ), - AccountAction::None, - )?; - - for c in 0..x { - let collator = create_account::( - "collator", - seed.take(), - AccountBalance::MinCandidateStake, - AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: c + 1 }, - )?; - - let mut decreasing_balance = >::new( - T::MinDelegation::get() * 2000u32.into(), - 1u32.into(), - ); - let mut col_del_count = 0u32; - for i in 0..T::MaxTopDelegationsPerCandidate::get() - 1 { - let del = create_account::( - "delegator", - seed.take(), - AccountBalance::Value(decreasing_balance.take()), - AccountAction::Delegate{ - collator: collator.clone(), - amount: Amount::All, - auto_compound: Percent::from_percent(100), - collator_delegation_count: col_del_count, - collator_auto_compound_delegation_count: col_del_count, - }, - )?; - col_del_count += 1; - - Pallet::::schedule_delegator_bond_less( - RawOrigin::Signed(del).into(), - collator.clone(), - 5u32.into(), - )?; - } - - let last_top_delegator_bond = decreasing_balance.take(); - >::delegate_with_auto_compound( - RawOrigin::Signed(delegator.clone()).into(), - collator.clone(), - last_top_delegator_bond, - Percent::from_percent(100), - col_del_count, - col_del_count, - c, - )?; - col_del_count += 1; - - assert_eq!( - >::get(&collator).map(|d| d.delegations.len()).unwrap_or_default(), - 0, - ); - - for i in 0..T::MaxBottomDelegationsPerCandidate::get() { - let del = create_account::( - "delegator", - seed.take(), - AccountBalance::Value(decreasing_balance.take()), - AccountAction::Delegate{ - collator: collator.clone(), - amount: Amount::All, - auto_compound: Percent::from_percent(100), - collator_delegation_count: col_del_count, - collator_auto_compound_delegation_count: col_del_count, - }, - )?; - col_del_count += 1; - - Pallet::::schedule_revoke_delegation( - RawOrigin::Signed(del).into(), - collator.clone(), - )?; - } - - assert_eq!( - >::get(&collator) - .map(|bd| bd.delegations.len() as u32) - .unwrap_or_default(), - T::MaxBottomDelegationsPerCandidate::get(), - ); - } - - Pallet::::schedule_leave_delegators(RawOrigin::Signed(delegator.clone()).into())?; - }: { - >::cancel_leave_delegators(RawOrigin::Signed(delegator.clone()).into())?; - } verify { - assert!(Pallet::::delegator_state(&delegator).expect("must exist").is_active()); - } - - execute_leave_delegators_worst { - // We assume that all candidates have full delegations with scheduled requests - let x in 1..T::MaxDelegationsPerDelegator::get(); - - let mut seed = Seed::new(); - - let delegator = create_account::( - "delegator", - seed.take(), - AccountBalance::Value( - T::MinDelegation::get() * T::MaxDelegationsPerDelegator::get().into() * 3000u32.into() - ), - AccountAction::None, - )?; - - let author = create_account::( - "collator", - seed.take(), - AccountBalance::MinCandidateStake, - AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: 1u32 }, - )?; - - for c in 0..x { - let collator = create_account::( - "collator", - seed.take(), - AccountBalance::MinCandidateStake, - AccountAction::JoinCandidates{ amount: Amount::All, candidate_count: c + 2 }, - )?; - - let mut decreasing_balance = >::new( - T::MinDelegation::get() * 2000u32.into(), - 1u32.into(), - ); - let mut col_del_count = 0u32; - for i in 0..T::MaxTopDelegationsPerCandidate::get() - 1 { - let del = create_account::( - "delegator", - seed.take(), - AccountBalance::Value(decreasing_balance.take()), - AccountAction::Delegate{ - collator: collator.clone(), - amount: Amount::All, - auto_compound: Percent::from_percent(100), - collator_delegation_count: col_del_count, - collator_auto_compound_delegation_count: col_del_count, - }, - )?; - col_del_count += 1; - - Pallet::::schedule_delegator_bond_less( - RawOrigin::Signed(del).into(), - collator.clone(), - 5u32.into(), - )?; - } - - let last_top_delegator_bond = decreasing_balance.take(); - >::delegate_with_auto_compound( - RawOrigin::Signed(delegator.clone()).into(), - collator.clone(), - last_top_delegator_bond, - Percent::from_percent(100), - col_del_count, - col_del_count, - c, - )?; - col_del_count += 1; - - assert_eq!( - >::get(&collator).map(|d| d.delegations.len()).unwrap_or_default(), - 0, - ); - - for i in 0..T::MaxBottomDelegationsPerCandidate::get() { - let del = create_account::( - "delegator", - seed.take(), - AccountBalance::Value(decreasing_balance.take()), - AccountAction::Delegate{ - collator: collator.clone(), - amount: Amount::All, - auto_compound: Percent::from_percent(100), - collator_delegation_count: col_del_count, - collator_auto_compound_delegation_count: col_del_count, - }, - )?; - col_del_count += 1; - - Pallet::::schedule_revoke_delegation( - RawOrigin::Signed(del).into(), - collator.clone(), - )?; - } - - assert_eq!( - >::get(&collator) - .map(|bd| bd.delegations.len() as u32) - .unwrap_or_default(), - T::MaxBottomDelegationsPerCandidate::get(), - ); - } - - Pallet::::schedule_leave_delegators(RawOrigin::Signed(delegator.clone()).into())?; - roll_to_and_author::(2, author.clone()); - }: { - >::execute_leave_delegators( - RawOrigin::Signed(delegator.clone()).into(), - delegator.clone(), - x, - )?; - } verify { - assert!(Pallet::::delegator_state(&delegator).is_none()); - } - schedule_revoke_delegation { // x controls the number of other scheduled requests let x in 0..( @@ -2543,27 +2224,6 @@ mod tests { }); } - #[test] - fn bench_schedule_leave_delegators() { - new_test_ext().execute_with(|| { - assert_ok!(Pallet::::test_benchmark_schedule_leave_delegators_worst()); - }); - } - - #[test] - fn bench_execute_leave_delegators() { - new_test_ext().execute_with(|| { - assert_ok!(Pallet::::test_benchmark_execute_leave_delegators_worst()); - }); - } - - #[test] - fn bench_cancel_leave_delegators() { - new_test_ext().execute_with(|| { - assert_ok!(Pallet::::test_benchmark_cancel_leave_delegators_worst()); - }); - } - #[test] fn bench_schedule_revoke_delegation() { new_test_ext().execute_with(|| { diff --git a/pallets/parachain-staking/src/delegation_requests.rs b/pallets/parachain-staking/src/delegation_requests.rs index 9f934c7ff11..5a392ef230b 100644 --- a/pallets/parachain-staking/src/delegation_requests.rs +++ b/pallets/parachain-staking/src/delegation_requests.rs @@ -21,7 +21,7 @@ use crate::pallet::{ Pallet, Round, RoundIndex, Total, }; use crate::weights::WeightInfo; -use crate::{auto_compound::AutoCompoundDelegations, AddGet, Delegator, DelegatorStatus}; +use crate::{auto_compound::AutoCompoundDelegations, AddGet, Delegator}; use frame_support::ensure; use frame_support::traits::Get; use frame_support::BoundedVec; @@ -32,7 +32,6 @@ use frame_support::{ use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::traits::Saturating; -use sp_std::vec; /// An action that can be performed upon a delegation #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, PartialOrd, Ord)] @@ -404,244 +403,6 @@ impl Pallet { } } - /// Schedules [DelegationAction::Revoke] for the delegator, towards all delegated collator. - /// The last fulfilled request causes the delegator to leave the set of delegators. - pub(crate) fn delegator_schedule_revoke_all( - delegator: T::AccountId, - ) -> DispatchResultWithPostInfo { - let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; - let actual_weight = - T::WeightInfo::schedule_leave_delegators_worst(state.delegations.0.len() as u32); - - let mut updated_scheduled_requests = vec![]; - let now = >::get().current; - let when = now.saturating_add(T::LeaveDelegatorsDelay::get()); - - // lazy migration for DelegatorStatus::Leaving - #[allow(deprecated)] - if matches!(state.status, DelegatorStatus::Leaving(_)) { - state.status = DelegatorStatus::Active; - >::insert(delegator.clone(), state.clone()); - } - - // it is assumed that a multiple delegations to the same collator does not exist, else this - // will cause a bug - the last duplicate delegation update will be the only one applied. - let mut existing_revoke_count = 0; - for bond in state.delegations.0.clone() { - let collator = bond.owner; - let bonded_amount = bond.amount; - let mut scheduled_requests = >::get(&collator); - - // cancel any existing requests - let request = - Self::cancel_request_with_state(&delegator, &mut state, &mut scheduled_requests); - let request = match request { - Some(revoke_req) if matches!(revoke_req.action, DelegationAction::Revoke(_)) => { - existing_revoke_count += 1; - revoke_req // re-insert the same Revoke request - } - _ => ScheduledRequest { - delegator: delegator.clone(), - action: DelegationAction::Revoke(bonded_amount.clone()), - when_executable: when, - }, - }; - - scheduled_requests - .try_push(request) - .map_err(|_| DispatchErrorWithPostInfo { - post_info: Some(actual_weight).into(), - error: Error::::ExceedMaxDelegationsPerDelegator.into(), - })?; - state.less_total = state.less_total.saturating_add(bonded_amount); - updated_scheduled_requests.push((collator, scheduled_requests)); - } - - if existing_revoke_count == state.delegations.0.len() { - return Err(DispatchErrorWithPostInfo { - post_info: Some(actual_weight).into(), - error: >::DelegatorAlreadyLeaving.into(), - }); - } - - updated_scheduled_requests - .into_iter() - .for_each(|(collator, scheduled_requests)| { - >::insert(collator, scheduled_requests); - }); - - >::insert(delegator.clone(), state); - Self::deposit_event(Event::DelegatorExitScheduled { - round: now, - delegator, - scheduled_exit: when, - }); - Ok(Some(actual_weight).into()) - } - - /// Cancels every [DelegationAction::Revoke] request for a delegator towards a collator. - /// Each delegation must have a [DelegationAction::Revoke] scheduled that must be allowed to be - /// executed in the current round, for this function to succeed. - pub(crate) fn delegator_cancel_scheduled_revoke_all( - delegator: T::AccountId, - ) -> DispatchResultWithPostInfo { - let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; - let mut updated_scheduled_requests = vec![]; - - let actual_weight = - T::WeightInfo::cancel_leave_delegators_worst(state.delegations.0.len() as u32); - - // backwards compatible handling for DelegatorStatus::Leaving - #[allow(deprecated)] - if matches!(state.status, DelegatorStatus::Leaving(_)) { - state.status = DelegatorStatus::Active; - >::insert(delegator.clone(), state.clone()); - Self::deposit_event(Event::DelegatorExitCancelled { delegator }); - return Ok(Some(actual_weight).into()); - } - - // pre-validate that all delegations have a Revoke request. - for bond in &state.delegations.0 { - let collator = bond.owner.clone(); - let scheduled_requests = >::get(&collator); - scheduled_requests - .iter() - .find(|req| { - req.delegator == delegator && matches!(req.action, DelegationAction::Revoke(_)) - }) - .ok_or(DispatchErrorWithPostInfo { - post_info: Some(actual_weight).into(), - error: >::DelegatorNotLeaving.into(), - })?; - } - - // cancel all requests - for bond in state.delegations.0.clone() { - let collator = bond.owner.clone(); - let mut scheduled_requests = >::get(&collator); - Self::cancel_request_with_state(&delegator, &mut state, &mut scheduled_requests); - updated_scheduled_requests.push((collator, scheduled_requests)); - } - - updated_scheduled_requests - .into_iter() - .for_each(|(collator, scheduled_requests)| { - >::insert(collator, scheduled_requests); - }); - - >::insert(delegator.clone(), state); - Self::deposit_event(Event::DelegatorExitCancelled { delegator }); - - Ok(Some(actual_weight).into()) - } - - /// Executes every [DelegationAction::Revoke] request for a delegator towards a collator. - /// Each delegation must have a [DelegationAction::Revoke] scheduled that must be allowed to be - /// executed in the current round, for this function to succeed. - pub(crate) fn delegator_execute_scheduled_revoke_all( - delegator: T::AccountId, - delegation_count: u32, - ) -> DispatchResultWithPostInfo { - let mut state = >::get(&delegator).ok_or(>::DelegatorDNE)?; - ensure!( - delegation_count >= (state.delegations.0.len() as u32), - Error::::TooLowDelegationCountToLeaveDelegators - ); - let now = >::get().current; - - // backwards compatible handling for DelegatorStatus::Leaving - #[allow(deprecated)] - if let DelegatorStatus::Leaving(when) = state.status { - ensure!( - >::get().current >= when, - Error::::DelegatorCannotLeaveYet - ); - - for bond in state.delegations.0.clone() { - if let Err(error) = Self::delegator_leaves_candidate( - bond.owner.clone(), - delegator.clone(), - bond.amount, - ) { - log::warn!( - "STORAGE CORRUPTED \nDelegator leaving collator failed with error: {:?}", - error - ); - } - - Self::delegation_remove_request_with_state(&bond.owner, &delegator, &mut state); - >::remove_auto_compound(&bond.owner, &delegator); - } - >::remove(&delegator); - Self::deposit_event(Event::DelegatorLeft { - delegator, - unstaked_amount: state.total, - }); - return Ok(().into()); - } - - let mut validated_scheduled_requests = vec![]; - // pre-validate that all delegations have a Revoke request that can be executed now. - for bond in &state.delegations.0 { - let scheduled_requests = >::get(&bond.owner); - let request_idx = scheduled_requests - .iter() - .position(|req| { - req.delegator == delegator && matches!(req.action, DelegationAction::Revoke(_)) - }) - .ok_or(>::DelegatorNotLeaving)?; - let request = &scheduled_requests[request_idx]; - - ensure!( - request.when_executable <= now, - >::DelegatorCannotLeaveYet - ); - - validated_scheduled_requests.push((bond.clone(), scheduled_requests, request_idx)) - } - - let mut updated_scheduled_requests = vec![]; - // we do not update the delegator state, since the it will be completely removed - for (bond, mut scheduled_requests, request_idx) in validated_scheduled_requests { - let collator = bond.owner; - - if let Err(error) = - Self::delegator_leaves_candidate(collator.clone(), delegator.clone(), bond.amount) - { - log::warn!( - "STORAGE CORRUPTED \nDelegator {:?} leaving collator failed with error: {:?}", - delegator, - error - ); - } - - // remove the scheduled request, since it is fulfilled - scheduled_requests.remove(request_idx).action.amount(); - updated_scheduled_requests.push((collator.clone(), scheduled_requests)); - - // remove the auto-compounding entry for the delegation - >::remove_auto_compound(&collator, &delegator); - } - - // set state.total so that state.adjust_bond_lock will remove lock - let unstaked_amount = state.total(); - state.total_sub::(unstaked_amount)?; - - updated_scheduled_requests - .into_iter() - .for_each(|(collator, scheduled_requests)| { - >::insert(collator, scheduled_requests); - }); - - Self::deposit_event(Event::DelegatorLeft { - delegator: delegator.clone(), - unstaked_amount, - }); - >::remove(&delegator); - - Ok(().into()) - } - /// Removes the delegator's existing [ScheduledRequest] towards a given collator, if exists. /// The state needs to be persisted by the caller of this function. pub(crate) fn delegation_remove_request_with_state( diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index 292200a5ed3..73486ab2486 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -82,6 +82,7 @@ pub mod pallet { }; use crate::{set::BoundedOrderedSet, traits::*, types::*, InflationInfo, Range, WeightInfo}; use crate::{AutoCompoundConfig, AutoCompoundDelegations}; + use frame_support::fail; use frame_support::pallet_prelude::*; use frame_support::traits::{ tokens::WithdrawReasons, Currency, Get, Imbalance, LockIdentifier, LockableCurrency, @@ -231,6 +232,7 @@ pub mod pallet { TooLowCandidateCountWeightHintGoOffline, CandidateLimitReached, CannotSetAboveMaxCandidates, + RemovedCall, } #[pallet::event] @@ -1220,42 +1222,25 @@ pub mod pallet { ) } - /// DEPRECATED use batch util with schedule_revoke_delegation for all delegations - /// Request to leave the set of delegators. If successful, the caller is scheduled to be - /// allowed to exit via a [DelegationAction::Revoke] towards all existing delegations. - /// Success forbids future delegation requests until the request is invoked or cancelled. + /// REMOVED, was schedule_leave_delegators #[pallet::call_index(19)] - #[pallet::weight(::WeightInfo::schedule_leave_delegators_worst( - T::MaxDelegationsPerDelegator::get() - ))] - pub fn schedule_leave_delegators(origin: OriginFor) -> DispatchResultWithPostInfo { - let delegator = ensure_signed(origin)?; - Self::delegator_schedule_revoke_all(delegator) + #[pallet::weight(::WeightInfo::set_staking_expectations())] + pub fn removed_call_19(_origin: OriginFor) -> DispatchResultWithPostInfo { + fail!(Error::::RemovedCall) } - /// DEPRECATED use batch util with execute_delegation_request for all delegations - /// Execute the right to exit the set of delegators and revoke all ongoing delegations. + /// REMOVED, was execute_leave_delegators #[pallet::call_index(20)] - #[pallet::weight(::WeightInfo::execute_leave_delegators_worst(*delegation_count))] - pub fn execute_leave_delegators( - origin: OriginFor, - delegator: T::AccountId, - delegation_count: u32, - ) -> DispatchResultWithPostInfo { - ensure_signed(origin)?; - Self::delegator_execute_scheduled_revoke_all(delegator, delegation_count) + #[pallet::weight(::WeightInfo::set_staking_expectations())] + pub fn removed_call_20(_origin: OriginFor) -> DispatchResultWithPostInfo { + fail!(Error::::RemovedCall) } - /// DEPRECATED use batch util with cancel_delegation_request for all delegations - /// Cancel a pending request to exit the set of delegators. Success clears the pending exit - /// request (thereby resetting the delay upon another `leave_delegators` call). + /// REMOVED, was cancel_leave_delegators #[pallet::call_index(21)] - #[pallet::weight(::WeightInfo::cancel_leave_delegators_worst( - T::MaxDelegationsPerDelegator::get() - ))] - pub fn cancel_leave_delegators(origin: OriginFor) -> DispatchResultWithPostInfo { - let delegator = ensure_signed(origin)?; - Self::delegator_cancel_scheduled_revoke_all(delegator) + #[pallet::weight(::WeightInfo::set_staking_expectations())] + pub fn removed_call_21(_origin: OriginFor) -> DispatchResultWithPostInfo { + fail!(Error::::RemovedCall) } /// Request to revoke an existing delegation. If successful, the delegation is scheduled diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index f34cf87b5c0..2d86bcb2bdc 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -30,10 +30,10 @@ use crate::mock::{ }; use crate::{ assert_events_emitted, assert_events_emitted_match, assert_events_eq, assert_no_events, - AtStake, Bond, CollatorStatus, DelegationScheduledRequests, DelegatorAdded, DelegatorState, - DelegatorStatus, Error, Event, Range, DELEGATOR_LOCK_ID, + AtStake, Bond, CollatorStatus, DelegationScheduledRequests, DelegatorAdded, Error, Event, + Range, DELEGATOR_LOCK_ID, }; -use frame_support::{assert_noop, assert_ok, BoundedVec}; +use frame_support::{assert_err, assert_noop, assert_ok, BoundedVec}; use sp_runtime::{traits::Zero, DispatchError, ModuleError, Perbill, Percent}; // ~~ ROOT ~~ @@ -2053,8 +2053,9 @@ fn can_still_delegate_if_leaving() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1, )); assert_ok!(ParachainStaking::delegate( RuntimeOrigin::signed(2), @@ -2162,464 +2163,44 @@ fn insufficient_delegate_weight_hint_fails() { (2, 20), (3, 20), (4, 20), - (5, 20), - (6, 20), - (7, 20), - (8, 20), - (9, 20), - (10, 20), - ]) - .with_candidates(vec![(1, 20), (2, 20)]) - .with_delegations(vec![(3, 1, 10), (4, 1, 10), (5, 1, 10), (6, 1, 10)]) - .build() - .execute_with(|| { - let mut count = 3u32; - for i in 7..11 { - assert_noop!( - ParachainStaking::delegate(RuntimeOrigin::signed(i), 1, 10, count, 0u32), - Error::::TooLowCandidateDelegationCountToDelegate - ); - } - // to set up for next error test - count = 4u32; - for i in 7..11 { - assert_ok!(ParachainStaking::delegate( - RuntimeOrigin::signed(i), - 1, - 10, - count, - 0u32 - )); - count += 1u32; - } - count = 0u32; - for i in 3..11 { - assert_noop!( - ParachainStaking::delegate(RuntimeOrigin::signed(i), 2, 10, count, 0u32), - Error::::TooLowDelegationCountToDelegate - ); - count += 1u32; - } - }); -} - -// SCHEDULE LEAVE DELEGATORS - -#[test] -fn schedule_leave_delegators_event_emits_correctly() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_events_eq!(Event::DelegatorExitScheduled { - round: 1, - delegator: 2, - scheduled_exit: 3 - }); - }); -} - -#[test] -fn cannot_schedule_leave_delegators_if_already_leaving() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_noop!( - ParachainStaking::schedule_leave_delegators(RuntimeOrigin::signed(2)) - .map_err(|err| err.error), - Error::::DelegatorAlreadyLeaving - ); - }); -} - -#[test] -fn cannot_schedule_leave_delegators_if_not_delegator() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .build() - .execute_with(|| { - assert_noop!( - ParachainStaking::schedule_leave_delegators(RuntimeOrigin::signed(2)), - Error::::DelegatorDNE - ); - }); -} - -// EXECUTE LEAVE DELEGATORS - -#[test] -fn execute_leave_delegators_event_emits_correctly() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 1 - )); - assert_events_emitted!(Event::DelegatorLeft { - delegator: 2, - unstaked_amount: 10 - }); - }); -} - -#[test] -fn execute_leave_delegators_unreserves_balance() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_eq!( - ParachainStaking::get_delegator_stakable_free_balance(&2), - 00 - ); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 1 - )); - assert_eq!( - ParachainStaking::get_delegator_stakable_free_balance(&2), - 10 - ); - assert_eq!(crate::mock::query_lock_amount(2, DELEGATOR_LOCK_ID), None); - }); -} - -#[test] -fn execute_leave_delegators_decreases_total_staked() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_eq!(ParachainStaking::total(), 40); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 1 - )); - assert_eq!(ParachainStaking::total(), 30); - }); -} - -#[test] -fn execute_leave_delegators_removes_delegator_state() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert!(ParachainStaking::delegator_state(2).is_some()); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 1 - )); - assert!(ParachainStaking::delegator_state(2).is_none()); - }); -} - -#[test] -fn execute_leave_delegators_removes_pending_delegation_requests() { - ExtBuilder::default() - .with_balances(vec![(1, 10), (2, 15)]) - .with_candidates(vec![(1, 10)]) - .with_delegations(vec![(2, 1, 15)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_delegator_bond_less( - RuntimeOrigin::signed(2), - 1, - 5 - )); - let state = ParachainStaking::delegation_scheduled_requests(&1); - assert_eq!( - state, - vec![ScheduledRequest { - delegator: 2, - when_executable: 3, - action: DelegationAction::Decrease(5), - }], - ); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 1 - )); - assert!(ParachainStaking::delegator_state(2).is_none()); - assert!( - !ParachainStaking::delegation_scheduled_requests(&1) - .iter() - .any(|x| x.delegator == 2), - "delegation request not removed" - ) - }); -} - -#[test] -fn execute_leave_delegators_removes_delegations_from_collator_state() { - ExtBuilder::default() - .with_balances(vec![(1, 100), (2, 20), (3, 20), (4, 20), (5, 20)]) - .with_candidates(vec![(2, 20), (3, 20), (4, 20), (5, 20)]) - .with_delegations(vec![(1, 2, 10), (1, 3, 10), (1, 4, 10), (1, 5, 10)]) - .build() - .execute_with(|| { - for i in 2..6 { - let candidate_state = - ParachainStaking::candidate_info(i).expect("initialized in ext builder"); - assert_eq!(candidate_state.total_counted, 30); - let top_delegations = - ParachainStaking::top_delegations(i).expect("initialized in ext builder"); - assert_eq!(top_delegations.delegations[0].owner, 1); - assert_eq!(top_delegations.delegations[0].amount, 10); - assert_eq!(top_delegations.total, 10); - } - assert_eq!( - ParachainStaking::delegator_state(1) - .unwrap() - .delegations - .0 - .len(), - 4usize - ); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(1) - )); - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(1), - 1, - 10 - )); - for i in 2..6 { - let candidate_state = - ParachainStaking::candidate_info(i).expect("initialized in ext builder"); - assert_eq!(candidate_state.total_counted, 20); - let top_delegations = - ParachainStaking::top_delegations(i).expect("initialized in ext builder"); - assert!(top_delegations.delegations.is_empty()); - } - }); -} - -#[test] -fn cannot_execute_leave_delegators_before_delay() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_noop!( - ParachainStaking::execute_leave_delegators(RuntimeOrigin::signed(2), 2, 1), - Error::::DelegatorCannotLeaveYet - ); - // can execute after delay - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 1 - )); - }); -} - -#[test] -fn cannot_execute_leave_delegators_if_single_delegation_revoke_manually_cancelled() { - 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(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_ok!(ParachainStaking::cancel_delegation_request( - RuntimeOrigin::signed(2), - 3 - )); - roll_to(10); - assert_noop!( - ParachainStaking::execute_leave_delegators(RuntimeOrigin::signed(2), 2, 2), - Error::::DelegatorNotLeaving - ); - // can execute after manually scheduling revoke, and the round delay after which - // all revokes can be executed - assert_ok!(ParachainStaking::schedule_revoke_delegation( - RuntimeOrigin::signed(2), - 3 - )); - roll_to(20); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 2 - )); - }); -} - -#[test] -fn insufficient_execute_leave_delegators_weight_hint_fails() { - ExtBuilder::default() - .with_balances(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20), (6, 20)]) - .with_candidates(vec![(1, 20)]) - .with_delegations(vec![(3, 1, 10), (4, 1, 10), (5, 1, 10), (6, 1, 10)]) - .build() - .execute_with(|| { - for i in 3..7 { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(i) - )); - } - roll_to(10); - for i in 3..7 { - assert_noop!( - ParachainStaking::execute_leave_delegators(RuntimeOrigin::signed(i), i, 0), - Error::::TooLowDelegationCountToLeaveDelegators - ); - } - }); -} - -#[test] -fn sufficient_execute_leave_delegators_weight_hint_succeeds() { - ExtBuilder::default() - .with_balances(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 20), (6, 20)]) - .with_candidates(vec![(1, 20)]) - .with_delegations(vec![(3, 1, 10), (4, 1, 10), (5, 1, 10), (6, 1, 10)]) - .build() - .execute_with(|| { - for i in 3..7 { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(i) - )); - } - roll_to(10); - for i in 3..7 { - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(i), - i, - 1 - )); - } - }); -} - -// CANCEL LEAVE DELEGATORS - -#[test] -fn cancel_leave_delegators_emits_correct_event() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_ok!(ParachainStaking::cancel_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_events_emitted!(Event::DelegatorExitCancelled { delegator: 2 }); - }); -} - -#[test] -fn cancel_leave_delegators_updates_delegator_state() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 10)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_ok!(ParachainStaking::cancel_leave_delegators( - RuntimeOrigin::signed(2) - )); - let delegator = - ParachainStaking::delegator_state(&2).expect("just cancelled exit so exists"); - assert!(delegator.is_active()); - }); -} - -#[test] -fn cannot_cancel_leave_delegators_if_single_delegation_revoke_manually_cancelled() { - 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(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_ok!(ParachainStaking::cancel_delegation_request( - RuntimeOrigin::signed(2), - 3 - )); - roll_to(10); - assert_noop!( - ParachainStaking::cancel_leave_delegators(RuntimeOrigin::signed(2)) - .map_err(|err| err.error), - Error::::DelegatorNotLeaving - ); - // can execute after manually scheduling revoke, without waiting for round delay after - // which all revokes can be executed - assert_ok!(ParachainStaking::schedule_revoke_delegation( - RuntimeOrigin::signed(2), - 3 - )); - assert_ok!(ParachainStaking::cancel_leave_delegators( - RuntimeOrigin::signed(2) - )); + (5, 20), + (6, 20), + (7, 20), + (8, 20), + (9, 20), + (10, 20), + ]) + .with_candidates(vec![(1, 20), (2, 20)]) + .with_delegations(vec![(3, 1, 10), (4, 1, 10), (5, 1, 10), (6, 1, 10)]) + .build() + .execute_with(|| { + let mut count = 3u32; + for i in 7..11 { + assert_noop!( + ParachainStaking::delegate(RuntimeOrigin::signed(i), 1, 10, count, 0u32), + Error::::TooLowCandidateDelegationCountToDelegate + ); + } + // to set up for next error test + count = 4u32; + for i in 7..11 { + assert_ok!(ParachainStaking::delegate( + RuntimeOrigin::signed(i), + 1, + 10, + count, + 0u32 + )); + count += 1u32; + } + count = 0u32; + for i in 3..11 { + assert_noop!( + ParachainStaking::delegate(RuntimeOrigin::signed(i), 2, 10, count, 0u32), + Error::::TooLowDelegationCountToDelegate + ); + count += 1u32; + } }); } @@ -2686,25 +2267,6 @@ fn can_revoke_delegation_if_revoking_another_delegation() { }); } -#[test] -fn delegator_not_allowed_revoke_if_already_leaving() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 20), (3, 20)]) - .with_candidates(vec![(1, 30), (3, 20)]) - .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_noop!( - ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(2), 3) - .map_err(|err| err.error), - >::PendingDelegationRequestAlreadyExists, - ); - }); -} - #[test] fn cannot_revoke_delegation_if_not_delegator() { ExtBuilder::default().build().execute_with(|| { @@ -3030,25 +2592,6 @@ fn delegator_bond_less_updates_delegator_state() { }); } -#[test] -fn delegator_not_allowed_bond_less_if_leaving() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 15)]) - .with_candidates(vec![(1, 30)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_noop!( - ParachainStaking::schedule_delegator_bond_less(RuntimeOrigin::signed(2), 1, 1) - .map_err(|err| err.error), - >::PendingDelegationRequestAlreadyExists, - ); - }); -} - #[test] fn cannot_delegator_bond_less_if_revoking() { ExtBuilder::default() @@ -4202,11 +3745,12 @@ fn parachain_bond_inflation_reserve_matches_config() { set_author(5, 1, 100); // 1. ensure delegators are paid for 2 rounds after they leave assert_noop!( - ParachainStaking::schedule_leave_delegators(RuntimeOrigin::signed(66)), + ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(66), 1), Error::::DelegatorDNE ); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(6) + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(6), + 1, )); assert_events_eq!( Event::ReservedForParachainBond { @@ -4328,7 +3872,7 @@ fn parachain_bond_inflation_reserve_matches_config() { }, ); roll_to_round_begin(6); - assert_ok!(ParachainStaking::execute_leave_delegators( + assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(6), 6, 10 @@ -5660,15 +5204,17 @@ fn payouts_follow_delegation_changes() { roll_blocks(1); // 1. ensure delegators are paid for 2 rounds after they leave assert_noop!( - ParachainStaking::schedule_leave_delegators(RuntimeOrigin::signed(66)), + ParachainStaking::schedule_revoke_delegation(RuntimeOrigin::signed(66), 1), Error::::DelegatorDNE ); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(6) + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(6), + 1, )); - assert_events_eq!(Event::DelegatorExitScheduled { + assert_events_eq!(Event::DelegationRevocationScheduled { round: 4, delegator: 6, + candidate: 1, scheduled_exit: 6, }); // fast forward to block in which delegator 6 exit executes @@ -5722,10 +5268,10 @@ fn payouts_follow_delegation_changes() { ); // keep paying 6 (note: inflation is in terms of total issuance so that's why 1 is 21) roll_to_round_begin(6); - assert_ok!(ParachainStaking::execute_leave_delegators( + assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(6), 6, - 10 + 1, )); assert_events_eq!( Event::CollatorChosen { @@ -5760,6 +5306,11 @@ fn payouts_follow_delegation_changes() { unstaked_amount: 10, total_candidate_staked: 40, }, + Event::DelegationRevoked { + delegator: 6, + candidate: 1, + unstaked_amount: 10, + }, Event::DelegatorLeft { delegator: 6, unstaked_amount: 10, @@ -7387,14 +6938,28 @@ fn test_delegator_scheduled_for_leave_is_rewarded_for_previous_rounds_but_not_fo // preset rewards for rounds 1, 2 and 3 (1..=3).for_each(|round| set_author(round, 1, 1)); - assert_ok!(ParachainStaking::schedule_leave_delegators( + assert_ok!(ParachainStaking::schedule_revoke_delegation( RuntimeOrigin::signed(2), + 1, )); - assert_events_eq!(Event::DelegatorExitScheduled { - round: 1, - delegator: 2, - scheduled_exit: 3, - }); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 3, + )); + assert_events_eq!( + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + }, + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 3, + scheduled_exit: 3, + }, + ); let collator = ParachainStaking::candidate_info(1).expect("candidate must exist"); assert_eq!( 1, collator.delegation_count, @@ -7451,14 +7016,28 @@ fn test_delegator_scheduled_for_leave_is_rewarded_when_request_cancelled() { // preset rewards for rounds 2, 3 and 4 (2..=4).for_each(|round| set_author(round, 1, 1)); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1, )); - assert_events_eq!(Event::DelegatorExitScheduled { - round: 1, - delegator: 2, - scheduled_exit: 3, - }); + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 3, + )); + assert_events_eq!( + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 1, + scheduled_exit: 3, + }, + Event::DelegationRevocationScheduled { + round: 1, + delegator: 2, + candidate: 3, + scheduled_exit: 3, + }, + ); let collator = ParachainStaking::candidate_info(1).expect("candidate must exist"); assert_eq!( 1, collator.delegation_count, @@ -7470,8 +7049,13 @@ fn test_delegator_scheduled_for_leave_is_rewarded_when_request_cancelled() { ); roll_to_round_begin(2); - assert_ok!(ParachainStaking::cancel_leave_delegators( - RuntimeOrigin::signed(2) + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 1, + )); + assert_ok!(ParachainStaking::cancel_delegation_request( + RuntimeOrigin::signed(2), + 3, )); roll_to_round_begin(4); @@ -7778,124 +7362,6 @@ fn revoke_last_removes_lock() { }); } -#[allow(deprecated)] -#[test] -fn test_delegator_with_deprecated_status_leaving_can_schedule_leave_delegators_as_fix() { - ExtBuilder::default() - .with_balances(vec![(1, 20), (2, 40)]) - .with_candidates(vec![(1, 20)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - >::mutate(2, |value| { - value.as_mut().map(|state| { - state.status = DelegatorStatus::Leaving(2); - }) - }); - let state = >::get(2); - assert!(matches!(state.unwrap().status, DelegatorStatus::Leaving(_))); - - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert!(>::get(1) - .iter() - .any(|r| r.delegator == 2 && matches!(r.action, DelegationAction::Revoke(_)))); - assert_events_eq!(Event::DelegatorExitScheduled { - round: 1, - delegator: 2, - scheduled_exit: 3 - }); - - let state = >::get(2); - assert!(matches!(state.unwrap().status, DelegatorStatus::Active)); - }); -} - -#[allow(deprecated)] -#[test] -fn test_delegator_with_deprecated_status_leaving_can_cancel_leave_delegators_as_fix() { - ExtBuilder::default() - .with_balances(vec![(1, 20), (2, 40)]) - .with_candidates(vec![(1, 20)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - >::mutate(2, |value| { - value.as_mut().map(|state| { - state.status = DelegatorStatus::Leaving(2); - }) - }); - let state = >::get(2); - assert!(matches!(state.unwrap().status, DelegatorStatus::Leaving(_))); - - assert_ok!(ParachainStaking::cancel_leave_delegators( - RuntimeOrigin::signed(2) - )); - assert_events_eq!(Event::DelegatorExitCancelled { delegator: 2 }); - - let state = >::get(2); - assert!(matches!(state.unwrap().status, DelegatorStatus::Active)); - }); -} - -#[allow(deprecated)] -#[test] -fn test_delegator_with_deprecated_status_leaving_can_execute_leave_delegators_as_fix() { - ExtBuilder::default() - .with_balances(vec![(1, 20), (2, 40)]) - .with_candidates(vec![(1, 20)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - >::mutate(2, |value| { - value.as_mut().map(|state| { - state.status = DelegatorStatus::Leaving(2); - }) - }); - let state = >::get(2); - assert!(matches!(state.unwrap().status, DelegatorStatus::Leaving(_))); - - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 1 - )); - assert_events_emitted!(Event::DelegatorLeft { - delegator: 2, - unstaked_amount: 10 - }); - - let state = >::get(2); - assert!(state.is_none()); - }); -} - -#[allow(deprecated)] -#[test] -fn test_delegator_with_deprecated_status_leaving_cannot_execute_leave_delegators_early_no_fix() { - ExtBuilder::default() - .with_balances(vec![(1, 20), (2, 40)]) - .with_candidates(vec![(1, 20)]) - .with_delegations(vec![(2, 1, 10)]) - .build() - .execute_with(|| { - >::mutate(2, |value| { - value.as_mut().map(|state| { - state.status = DelegatorStatus::Leaving(2); - }) - }); - let state = >::get(2); - assert!(matches!(state.unwrap().status, DelegatorStatus::Leaving(_))); - - assert_noop!( - ParachainStaking::execute_leave_delegators(RuntimeOrigin::signed(2), 2, 1), - Error::::DelegatorCannotLeaveYet - ); - }); -} - #[test] fn test_set_auto_compound_fails_if_invalid_delegation_hint() { ExtBuilder::default() @@ -8127,65 +7593,24 @@ fn test_execute_leave_delegators_removes_auto_compounding_state() { 2, )); - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) - )); - roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( - RuntimeOrigin::signed(2), - 2, - 2, - )); - - assert!( - !ParachainStaking::auto_compounding_delegations(&1) - .iter() - .any(|x| x.delegator == 2), - "delegation auto-compound config was not removed" - ); - assert!( - !ParachainStaking::auto_compounding_delegations(&3) - .iter() - .any(|x| x.delegator == 2), - "delegation auto-compound config was not removed" - ); - }); -} - -#[allow(deprecated)] -#[test] -fn test_execute_leave_delegators_with_deprecated_status_leaving_removes_auto_compounding_state() { - ExtBuilder::default() - .with_balances(vec![(1, 30), (2, 20), (3, 20)]) - .with_candidates(vec![(1, 30), (3, 20)]) - .with_delegations(vec![(2, 1, 10), (2, 3, 10)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::set_auto_compound( + assert_ok!(ParachainStaking::schedule_revoke_delegation( RuntimeOrigin::signed(2), 1, - Percent::from_percent(50), - 0, - 2, )); - assert_ok!(ParachainStaking::set_auto_compound( + assert_ok!(ParachainStaking::schedule_revoke_delegation( RuntimeOrigin::signed(2), 3, - Percent::from_percent(50), - 0, - 2, )); - - >::mutate(2, |value| { - value.as_mut().map(|state| { - state.status = DelegatorStatus::Leaving(2); - }) - }); roll_to(10); - assert_ok!(ParachainStaking::execute_leave_delegators( + assert_ok!(ParachainStaking::execute_delegation_request( RuntimeOrigin::signed(2), 2, + 1, + )); + assert_ok!(ParachainStaking::execute_delegation_request( + RuntimeOrigin::signed(2), 2, + 3, )); assert!( @@ -8838,8 +8263,9 @@ fn test_delegate_with_auto_compound_can_still_delegate_to_other_if_leaving() { .with_delegations(vec![(2, 1, 10)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(2) + assert_ok!(ParachainStaking::schedule_revoke_delegation( + RuntimeOrigin::signed(2), + 1, )); assert_ok!(ParachainStaking::delegate_with_auto_compound( RuntimeOrigin::signed(2), @@ -9037,3 +8463,21 @@ fn test_compute_top_candidates_is_stable() { ); }); } + +#[test] +fn test_removed_calls() { + ExtBuilder::default().build().execute_with(|| { + assert_err!( + ParachainStaking::removed_call_19(RuntimeOrigin::root()), + Error::::RemovedCall + ); + assert_err!( + ParachainStaking::removed_call_20(RuntimeOrigin::root()), + Error::::RemovedCall + ); + assert_err!( + ParachainStaking::removed_call_21(RuntimeOrigin::root()), + Error::::RemovedCall + ); + }); +} diff --git a/precompiles/parachain-staking/StakingInterface.sol b/precompiles/parachain-staking/StakingInterface.sol index e83bfe4ca6d..ec939050ec9 100644 --- a/precompiles/parachain-staking/StakingInterface.sol +++ b/precompiles/parachain-staking/StakingInterface.sol @@ -238,26 +238,6 @@ interface ParachainStaking { uint256 delegatorDelegationCount ) external; - /// @notice DEPRECATED use batch util with scheduleRevokeDelegation for all delegations - /// @dev Request to leave the set of delegators - /// @custom:selector f939dadb - function scheduleLeaveDelegators() external; - - /// @notice DEPRECATED use batch util with executeDelegationRequest for all delegations - /// @dev Execute request to leave the set of delegators and revoke all delegations - /// @custom:selector fb1e2bf9 - /// @param delegator The leaving delegator - /// @param delegatorDelegationCount The number of active delegations to be revoked by delegator - function executeLeaveDelegators( - address delegator, - uint256 delegatorDelegationCount - ) external; - - /// @notice DEPRECATED use batch util with cancelDelegationRequest for all delegations - /// @dev Cancel request to leave the set of delegators - /// @custom:selector f7421284 - function cancelLeaveDelegators() external; - /// @dev Request to revoke an existing delegation /// @custom:selector 1a1c740c /// @param candidate The address of the collator candidate which will no longer be supported diff --git a/precompiles/parachain-staking/src/lib.rs b/precompiles/parachain-staking/src/lib.rs index b9ab2d68a50..11480d91581 100644 --- a/precompiles/parachain-staking/src/lib.rs +++ b/precompiles/parachain-staking/src/lib.rs @@ -727,58 +727,6 @@ where Ok(()) } - /// Deprecated in favor of batch util - #[precompile::public("scheduleLeaveDelegators()")] - #[precompile::public("schedule_leave_delegators()")] - fn schedule_leave_delegators(handle: &mut impl PrecompileHandle) -> EvmResult { - // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); - let call = pallet_parachain_staking::Call::::schedule_leave_delegators {}; - - // Dispatch call (if enough gas). - RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - - Ok(()) - } - - /// Deprecated in favor of batch util - #[precompile::public("executeLeaveDelegators(address,uint256)")] - #[precompile::public("execute_leave_delegators(address,uint256)")] - fn execute_leave_delegators( - handle: &mut impl PrecompileHandle, - delegator: Address, - delegator_delegation_count: Convert, - ) -> EvmResult { - let delegator = Runtime::AddressMapping::into_account_id(delegator.0); - let delegator_delegation_count = delegator_delegation_count.converted(); - - // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); - let call = pallet_parachain_staking::Call::::execute_leave_delegators { - delegator, - delegation_count: delegator_delegation_count, - }; - - // Dispatch call (if enough gas). - RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - - Ok(()) - } - - /// Deprecated in favor of batch util - #[precompile::public("cancelLeaveDelegators()")] - #[precompile::public("cancel_leave_delegators()")] - fn cancel_leave_delegators(handle: &mut impl PrecompileHandle) -> EvmResult { - // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); - let call = pallet_parachain_staking::Call::::cancel_leave_delegators {}; - - // Dispatch call (if enough gas). - RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - - Ok(()) - } - #[precompile::public("scheduleRevokeDelegation(address)")] #[precompile::public("schedule_revoke_delegation(address)")] fn schedule_revoke_delegation( diff --git a/precompiles/parachain-staking/src/tests.rs b/precompiles/parachain-staking/src/tests.rs index 41489a29cb4..0c287821b14 100644 --- a/precompiles/parachain-staking/src/tests.rs +++ b/precompiles/parachain-staking/src/tests.rs @@ -73,9 +73,6 @@ fn selectors() { assert!(PCall::execute_candidate_bond_less_selectors().contains(&0x2e290290)); assert!(PCall::cancel_candidate_bond_less_selectors().contains(&0xb5ad5f07)); assert!(PCall::delegate_selectors().contains(&0x829f5ee3)); - assert!(PCall::schedule_leave_delegators_selectors().contains(&0xf939dadb)); - assert!(PCall::execute_leave_delegators_selectors().contains(&0xfb1e2bf9)); - assert!(PCall::cancel_leave_delegators_selectors().contains(&0xf7421284)); assert!(PCall::schedule_revoke_delegation_selectors().contains(&0x1a1c740c)); assert!(PCall::delegator_bond_more_selectors().contains(&0x0465135b)); assert!(PCall::schedule_delegator_bond_less_selectors().contains(&0xc172fd2b)); @@ -116,9 +113,6 @@ fn modifiers() { tester.test_default_modifier(PCall::execute_candidate_bond_less_selectors()); tester.test_default_modifier(PCall::cancel_candidate_bond_less_selectors()); tester.test_default_modifier(PCall::delegate_selectors()); - tester.test_default_modifier(PCall::schedule_leave_delegators_selectors()); - tester.test_default_modifier(PCall::execute_leave_delegators_selectors()); - tester.test_default_modifier(PCall::cancel_leave_delegators_selectors()); tester.test_default_modifier(PCall::schedule_revoke_delegation_selectors()); tester.test_default_modifier(PCall::delegator_bond_more_selectors()); tester.test_default_modifier(PCall::schedule_delegator_bond_less_selectors()); @@ -1231,90 +1225,6 @@ fn delegate_works() { }); } -#[test] -fn schedule_leave_delegators_works() { - ExtBuilder::default() - .with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)]) - .with_candidates(vec![(Alice.into(), 1_000)]) - .with_delegations(vec![(Bob.into(), Alice.into(), 1_000)]) - .build() - .execute_with(|| { - let input_data = PCall::schedule_leave_delegators {}.into(); - - // Make sure the call goes through successfully - assert_ok!(RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root())); - - let expected: crate::mock::RuntimeEvent = StakingEvent::DelegatorExitScheduled { - round: 1, - delegator: Bob.into(), - scheduled_exit: 3, - } - .into(); - // Assert that the events vector contains the one expected - assert!(events().contains(&expected)); - }); -} - -#[test] -fn execute_leave_delegators_works() { - ExtBuilder::default() - .with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 500)]) - .with_candidates(vec![(Alice.into(), 1_000)]) - .with_delegations(vec![(Bob.into(), Alice.into(), 500)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(Bob.into()) - )); - roll_to(10); - - let input_data = PCall::execute_leave_delegators { - delegator: Address(Bob.into()), - delegator_delegation_count: 1.into(), - } - .into(); - - // Make sure the call goes through successfully - assert_ok!( - RuntimeCall::Evm(evm_call(Alice, input_data)).dispatch(RuntimeOrigin::root()) - ); - - let expected: crate::mock::RuntimeEvent = StakingEvent::DelegatorLeft { - delegator: Bob.into(), - unstaked_amount: 500, - } - .into(); - // Assert that the events vector contains the one expected - assert!(events().contains(&expected)); - }); -} - -#[test] -fn cancel_leave_delegators_works() { - ExtBuilder::default() - .with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 500)]) - .with_candidates(vec![(Alice.into(), 1_000)]) - .with_delegations(vec![(Bob.into(), Alice.into(), 500)]) - .build() - .execute_with(|| { - assert_ok!(ParachainStaking::schedule_leave_delegators( - RuntimeOrigin::signed(Bob.into()) - )); - - let input_data = PCall::cancel_leave_delegators {}.into(); - - // Make sure the call goes through successfully - assert_ok!(RuntimeCall::Evm(evm_call(Bob, input_data)).dispatch(RuntimeOrigin::root())); - - let expected: crate::mock::RuntimeEvent = StakingEvent::DelegatorExitCancelled { - delegator: Bob.into(), - } - .into(); - // Assert that the events vector contains the one expected - assert!(events().contains(&expected)); - }); -} - #[test] fn schedule_revoke_delegation_works() { ExtBuilder::default() @@ -1816,9 +1726,6 @@ fn test_deprecated_solidity_selectors_are_supported() { "candidate_bond_more(uint256)", "execute_candidate_bond_less(address)", "cancel_candidate_bond_less()", - "schedule_leave_delegators()", - "execute_leave_delegators(address,uint256)", - "cancel_leave_delegators()", "schedule_revoke_delegation(address)", "schedule_delegator_bond_less(address,uint256)", "delegator_bond_more(address,uint256)", diff --git a/tests/tests/test-staking/test-delegation-scheduled-requests.ts b/tests/tests/test-staking/test-delegation-scheduled-requests.ts index 1c84de92eb4..103e97ad916 100644 --- a/tests/tests/test-staking/test-delegation-scheduled-requests.ts +++ b/tests/tests/test-staking/test-delegation-scheduled-requests.ts @@ -679,76 +679,3 @@ describeDevMoonbeam("Staking - Delegation Scheduled Requests - collator leave", expect(delagationRequestsKeys.map((k) => k.args[0].toString())).to.deep.equal([alith.address]); }); }); - -describeDevMoonbeam("Staking - Delegation Scheduled Requests - delegator leave", (context) => { - before("should delegate and add baltathar as candidate", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith), - context.polkadotApi.tx.parachainStaking - .joinCandidates(MIN_GLMR_STAKING, 1) - .signAsync(baltathar), - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR + 10n, 0, 0) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock([ - context.polkadotApi.tx.parachainStaking - .delegate(baltathar.address, MIN_GLMR_DELEGATOR + 10n, 0, 1) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock([ - context.polkadotApi.tx.parachainStaking - .scheduleDelegatorBondLess(alith.address, 10n) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .scheduleDelegatorBondLess(baltathar.address, 10n) - .signAsync(ethan) - ) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - - const roundDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay.toNumber(); - await jumpRounds(context, roundDelay); - }); - - it("should remove complete scheduled requests across multiple candidates", async () => { - const delegationRequestsAlithBefore = - await context.polkadotApi.query.parachainStaking.delegationScheduledRequests(alith.address); - const delegationRequestsBaltatharBefore = - await context.polkadotApi.query.parachainStaking.delegationScheduledRequests( - baltathar.address - ); - expect(delegationRequestsAlithBefore.toJSON()).to.not.be.empty; - expect(delegationRequestsBaltatharBefore.toJSON()).to.not.be.empty; - - await context.createBlock( - context.polkadotApi.tx.parachainStaking - .executeLeaveDelegators(ethan.address, 2) - .signAsync(ethan) - ); - - const delegationRequestsAlithAfter = - await context.polkadotApi.query.parachainStaking.delegationScheduledRequests(alith.address); - const delegationRequestsBaltatharAfter = - await context.polkadotApi.query.parachainStaking.delegationScheduledRequests( - baltathar.address - ); - expect(delegationRequestsAlithAfter.toJSON()).to.be.empty; - expect(delegationRequestsBaltatharAfter.toJSON()).to.be.empty; - }); -}); diff --git a/tests/tests/test-staking/test-delegator-leave.ts b/tests/tests/test-staking/test-delegator-leave.ts deleted file mode 100644 index 79a86a7c5a1..00000000000 --- a/tests/tests/test-staking/test-delegator-leave.ts +++ /dev/null @@ -1,456 +0,0 @@ -import "@polkadot/api-augment"; -import "@moonbeam-network/api-augment"; -import { expect } from "chai"; -import { MIN_GLMR_STAKING, MIN_GLMR_DELEGATOR } from "../../util/constants"; -import { describeDevMoonbeam } from "../../util/setup-dev-tests"; -import { alith, baltathar, ethan } from "../../util/accounts"; -import { expectOk } from "../../util/expect"; -import { jumpRounds } from "../../util/block"; - -describeDevMoonbeam("Staking - Delegator Leave Schedule - already scheduled", (context) => { - before("should delegate", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan), - ]) - ); - - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - }); - - it("should fail", async () => { - const block = await context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ); - expect(block.result.successful).to.be.false; - expect(block.result.error.name).to.equal("DelegatorAlreadyLeaving"); - }); -}); - -describeDevMoonbeam("Staking - Delegator Leave Schedule - valid request", (context) => { - before("should delegate", async () => { - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan) - ) - ); - - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - }); - - it("should schedule revokes on all delegations", async () => { - const delegatorState = ( - await context.polkadotApi.query.parachainStaking.delegatorState(ethan.address) - ).unwrap(); - const currentRound = ( - await context.polkadotApi.query.parachainStaking.round() - ).current.toNumber(); - const roundDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay.toNumber(); - - for await (const delegation of delegatorState.delegations) { - const scheduledRequests = - (await context.polkadotApi.query.parachainStaking.delegationScheduledRequests( - delegation.owner - )) as unknown as any[]; - const revokeRequest = scheduledRequests.find( - (req) => req.delegator.eq(ethan.address) && req.action.isRevoke - ); - expect(revokeRequest).to.not.be.undefined; - expect(revokeRequest.whenExecutable.toNumber()).to.equal(currentRound + roundDelay); - } - }); -}); - -describeDevMoonbeam("Staking - Delegator Leave Execute - before round delay", (context) => { - before("should delegate, schedule leave, and jump to earlier round", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith), - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - - const leaveDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay; - await jumpRounds(context, leaveDelay.subn(1).toNumber()); - }); - - it("should fail", async () => { - const block = await context.createBlock( - context.polkadotApi.tx.parachainStaking - .executeLeaveDelegators(ethan.address, 1) - .signAsync(ethan) - ); - expect(block.result.successful).to.be.false; - expect(block.result.error.name).to.equal("DelegatorCannotLeaveYet"); - }); -}); - -describeDevMoonbeam("Staking - Delegator Leave - exact round delay", (context) => { - before("should delegate, schedule leave, and jump to exact round", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith), - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - - const leaveDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay; - await jumpRounds(context, leaveDelay.toNumber()); - }); - - it("should succeed", async () => { - const block = await context.createBlock( - context.polkadotApi.tx.parachainStaking - .executeLeaveDelegators(ethan.address, 1) - .signAsync(ethan) - ); - expect(block.result.successful).to.be.true; - const leaveEvents = block.result.events.reduce((acc, event) => { - if (context.polkadotApi.events.parachainStaking.DelegatorLeft.is(event.event)) { - acc.push({ - account: event.event.data[0].toString(), - }); - } - return acc; - }, []); - - expect(leaveEvents).to.deep.equal([ - { - account: ethan.address, - }, - ]); - }); -}); - -describeDevMoonbeam( - "Staking - Delegator Leave - executeLeaveDelegators executed after round delay", - (context) => { - const EXTRA_BOND_AMOUNT = 1_000_000_000_000_000_000n; - const BOND_AMOUNT = MIN_GLMR_STAKING + EXTRA_BOND_AMOUNT; - - before("should delegate, schedule leave, and jump to after round", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith), - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - - const leaveDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay; - await jumpRounds(context, leaveDelay.addn(5).toNumber()); - }); - - it("should succeed", async () => { - const block = await context.createBlock( - context.polkadotApi.tx.parachainStaking - .executeLeaveDelegators(ethan.address, 1) - .signAsync(ethan) - ); - expect(block.result.successful).to.be.true; - const leaveEvents = block.result.events.reduce((acc, event) => { - if (context.polkadotApi.events.parachainStaking.DelegatorLeft.is(event.event)) { - acc.push({ - account: event.event.data[0].toString(), - }); - } - return acc; - }, []); - - expect(leaveEvents).to.deep.equal([ - { - account: ethan.address, - }, - ]); - }); - } -); - -describeDevMoonbeam( - "Staking - Delegator Leave Cancel - one revoke manually cancelled", - (context) => { - before("should schedule leave then cancel single revoke", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith), - context.polkadotApi.tx.parachainStaking - .joinCandidates(MIN_GLMR_STAKING, 1) - .signAsync(baltathar), - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .delegate(baltathar.address, MIN_GLMR_DELEGATOR, 0, 1) - .signAsync(ethan) - ) - ); - - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - const leaveDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay; - await jumpRounds(context, leaveDelay.addn(1).toNumber()); - - // cancel single request - await expectOk( - context.createBlock([ - context.polkadotApi.tx.parachainStaking - .cancelDelegationRequest(baltathar.address) - .signAsync(ethan), - ]) - ); - }); - - it("should fail", async () => { - const block = await context.createBlock( - context.polkadotApi.tx.parachainStaking.cancelLeaveDelegators().signAsync(ethan) - ); - expect(block.result.error.name).to.equal("DelegatorNotLeaving"); - }); - } -); - -describeDevMoonbeam("Staking - Delegator Leave Cancel - manually reschedule revoke", (context) => { - before("should schedule leave then cancel single revoke then reschedule it", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith), - context.polkadotApi.tx.parachainStaking - .joinCandidates(MIN_GLMR_STAKING, 1) - .signAsync(baltathar), - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .delegate(baltathar.address, MIN_GLMR_DELEGATOR, 0, 1) - .signAsync(ethan) - ) - ); - - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - const leaveDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay; - await jumpRounds(context, leaveDelay.addn(1).toNumber()); - - // cancel single revoke request - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .cancelDelegationRequest(baltathar.address) - .signAsync(ethan) - ) - ); - - // reschedule single revoke request - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .scheduleRevokeDelegation(baltathar.address) - .signAsync(ethan) - ) - ); - }); - - it("should succeed", async () => { - const block = await context.createBlock( - context.polkadotApi.tx.parachainStaking.cancelLeaveDelegators().signAsync(ethan) - ); - expect(block.result.successful).to.be.true; - const leaveEvents = block.result.events.reduce((acc, event) => { - if (context.polkadotApi.events.parachainStaking.DelegatorExitCancelled.is(event.event)) { - acc.push({ - account: event.event.data[0].toString(), - }); - } - return acc; - }, []); - expect(leaveEvents).to.deep.equal([ - { - account: ethan.address, - }, - ]); - }); -}); - -describeDevMoonbeam("Staking - Delegator Leave Execute - revoke manually cancelled", (context) => { - before("should schedule leave then cancel single revoke", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith), - context.polkadotApi.tx.parachainStaking - .joinCandidates(MIN_GLMR_STAKING, 1) - .signAsync(baltathar), - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .delegate(baltathar.address, MIN_GLMR_DELEGATOR, 0, 1) - .signAsync(ethan) - ) - ); - - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - const leaveDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay; - await jumpRounds(context, leaveDelay.addn(1).toNumber()); - - // cancel single revoke request - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .cancelDelegationRequest(baltathar.address) - .signAsync(ethan) - ) - ); - }); - - it("should fail", async () => { - const block = await context.createBlock( - context.polkadotApi.tx.parachainStaking - .executeLeaveDelegators(ethan.address, 2) - .signAsync(ethan) - ); - expect(block.result.error.name).to.equal("DelegatorNotLeaving"); - }); -}); - -describeDevMoonbeam( - "Staking - Delegator Leave Execute - manually rescheduled revoke", - (context) => { - before("should schedule leave then cancel single revoke then reschedule it", async () => { - await expectOk( - context.createBlock([ - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith), - context.polkadotApi.tx.parachainStaking - .joinCandidates(MIN_GLMR_STAKING, 1) - .signAsync(baltathar), - context.polkadotApi.tx.parachainStaking - .delegate(alith.address, MIN_GLMR_DELEGATOR, 0, 0) - .signAsync(ethan), - ]) - ); - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .delegate(baltathar.address, MIN_GLMR_DELEGATOR, 0, 1) - .signAsync(ethan) - ) - ); - - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) - ) - ); - const leaveDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay; - await jumpRounds(context, leaveDelay.addn(1).toNumber()); - - // cancel single revoke request - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .cancelDelegationRequest(baltathar.address) - .signAsync(ethan) - ) - ); - - // reschedule single revoke request - await expectOk( - context.createBlock( - context.polkadotApi.tx.parachainStaking - .scheduleRevokeDelegation(baltathar.address) - .signAsync(ethan) - ) - ); - await jumpRounds(context, leaveDelay.addn(1).toNumber()); - }); - - it("should succeed", async () => { - const block = await context.createBlock( - context.polkadotApi.tx.parachainStaking - .executeLeaveDelegators(ethan.address, 2) - .signAsync(ethan) - ); - expect(block.result.successful).to.be.true; - const leaveEvents = block.result.events.reduce((acc, event) => { - if (context.polkadotApi.events.parachainStaking.DelegatorLeft.is(event.event)) { - acc.push({ - account: event.event.data[0].toString(), - }); - } - return acc; - }, []); - expect(leaveEvents).to.deep.equal([ - { - account: ethan.address, - }, - ]); - }); - } -); diff --git a/tests/tests/test-staking/test-rewards-auto-compound.ts b/tests/tests/test-staking/test-rewards-auto-compound.ts index f4a673659c8..bf4f7be79c8 100644 --- a/tests/tests/test-staking/test-rewards-auto-compound.ts +++ b/tests/tests/test-staking/test-rewards-auto-compound.ts @@ -188,7 +188,7 @@ describeDevMoonbeam("Staking - Rewards Auto-Compound - no revoke requests", (con describeDevMoonbeam( "Staking - Rewards Auto-Compound - scheduled revoke request after round snapshot", (context) => { - before("should scheduleLeaveDelegators", async () => { + before("should scheduleRevokeDelegation", async () => { await expectOk( context.createBlock([ context.polkadotApi.tx.sudo @@ -227,7 +227,7 @@ describeDevMoonbeam( } ); -describeDevMoonbeam("Staking - Rewards Auto-Compound - delegator leave", (context) => { +describeDevMoonbeam("Staking - Rewards Auto-Compound - delegator revoke", (context) => { before("should delegate and add baltathar as candidate", async () => { await expectOk( context.createBlock([ @@ -252,11 +252,16 @@ describeDevMoonbeam("Staking - Rewards Auto-Compound - delegator leave", (contex await expectOk( context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) + context.polkadotApi.tx.utility + .batch([ + context.polkadotApi.tx.parachainStaking.scheduleRevokeDelegation(alith.address), + context.polkadotApi.tx.parachainStaking.scheduleRevokeDelegation(baltathar.address), + ]) + .signAsync(ethan) ) ); - const roundDelay = context.polkadotApi.consts.parachainStaking.leaveDelegatorsDelay.toNumber(); + const roundDelay = context.polkadotApi.consts.parachainStaking.revokeDelegationDelay.toNumber(); await jumpRounds(context, roundDelay); }); @@ -270,10 +275,21 @@ describeDevMoonbeam("Staking - Rewards Auto-Compound - delegator leave", (contex expect(autoCompoundDelegationsAlithBefore.toJSON()).to.not.be.empty; expect(autoCompoundDelegationsBaltatharBefore.toJSON()).to.not.be.empty; - await context.createBlock( - context.polkadotApi.tx.parachainStaking - .executeLeaveDelegators(ethan.address, 2) - .signAsync(ethan) + await expectOk( + context.createBlock( + context.polkadotApi.tx.utility + .batch([ + context.polkadotApi.tx.parachainStaking.executeDelegationRequest( + ethan.address, + alith.address + ), + context.polkadotApi.tx.parachainStaking.executeDelegationRequest( + ethan.address, + baltathar.address + ), + ]) + .signAsync(ethan) + ) ); const autoCompoundDelegationsAlithAfter = diff --git a/tests/tests/test-staking/test-rewards.ts b/tests/tests/test-staking/test-rewards.ts index 24fa42de2fa..71f8088a7b9 100644 --- a/tests/tests/test-staking/test-rewards.ts +++ b/tests/tests/test-staking/test-rewards.ts @@ -43,8 +43,8 @@ describeDevMoonbeam("Staking - Rewards - no scheduled requests", (context) => { }); }); -describeDevMoonbeam("Staking - Rewards - scheduled leave request", (context) => { - before("should scheduleLeaveDelegators", async () => { +describeDevMoonbeam("Staking - Rewards - scheduled revoke request", (context) => { + before("should scheduleRevokeDelegation", async () => { await expectOk( context.createBlock([ context.polkadotApi.tx.sudo @@ -57,7 +57,9 @@ describeDevMoonbeam("Staking - Rewards - scheduled leave request", (context) => ); await expectOk( context.createBlock( - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(ethan) + context.polkadotApi.tx.parachainStaking + .scheduleRevokeDelegation(alith.address) + .signAsync(ethan) ) ); }); @@ -132,7 +134,7 @@ describeDevMoonbeam("Staking - Rewards - scheduled bond decrease request", (cont const EXTRA_BOND_AMOUNT = 1_000_000_000_000_000_000n; const BOND_AMOUNT = MIN_GLMR_STAKING + EXTRA_BOND_AMOUNT; - before("should scheduleLeaveDelegators", async () => { + before("should scheduleDelegatorBondLess", async () => { await expectOk( context.createBlock([ context.polkadotApi.tx.sudo diff --git a/tests/tests/test-staking/test-weights.ts b/tests/tests/test-staking/test-weights.ts index 17a9ec56d28..3432e9980f0 100644 --- a/tests/tests/test-staking/test-weights.ts +++ b/tests/tests/test-staking/test-weights.ts @@ -172,99 +172,6 @@ describeBenchmark("Staking - Max Transaction Fit", "scheduleRevokeDelegation", a ); }); -describeBenchmark("Staking - Max Transaction Fit", "scheduleLeaveDelegators", async (context) => { - const maxTransactions = 350; - const randomAccounts = await createAccounts(context, maxTransactions); - for (const randomAccountsChunk of chunk(randomAccounts, 20)) { - await expectOk( - context.createBlock( - randomAccountsChunk.map((account) => - context.polkadotApi.tx.parachainStaking - .delegateWithAutoCompound( - alith.address, - MIN_GLMR_DELEGATOR, - 100, - maxTransactions, - maxTransactions, - 0 - ) - .signAsync(account) - ) - ) - ); - } - - expect((await context.polkadotApi.query.parachainStaking.delegatorState.keys()).length).to.equal( - maxTransactions - ); - - await expectOk( - context.createBlock( - randomAccounts.map((account) => - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(account) - ) - ) - ); -}); - -describeBenchmark("Staking - Max Transaction Fit", "executeLeaveDelegators", async (context) => { - const maxTransactions = 350; - const randomAccounts = await createAccounts(context, maxTransactions); - - await expectOk( - context.createBlock( - context.polkadotApi.tx.sudo - .sudo(context.polkadotApi.tx.parachainStaking.setBlocksPerRound(10)) - .signAsync(alith) - ) - ); - - for (const randomAccountsChunk of chunk(randomAccounts, 20)) { - await expectOk( - context.createBlock( - randomAccountsChunk.map((account) => - context.polkadotApi.tx.parachainStaking - .delegateWithAutoCompound( - alith.address, - MIN_GLMR_DELEGATOR, - 100, - maxTransactions, - maxTransactions, - 0 - ) - .signAsync(account) - ) - ) - ); - } - - expect((await context.polkadotApi.query.parachainStaking.delegatorState.keys()).length).to.equal( - maxTransactions - ); - - for (const randomAccountsChunk of chunk(randomAccounts, 20)) { - await expectOk( - context.createBlock( - randomAccountsChunk.map((account) => - context.polkadotApi.tx.parachainStaking.scheduleLeaveDelegators().signAsync(account) - ) - ) - ); - } - - await jumpRounds(context, 3); - - await expectOk( - context.createBlock( - randomAccounts.map((account) => - context.polkadotApi.tx.parachainStaking - .executeLeaveDelegators(account.address, 1) - .signAsync(account) - ) - ) - ); -}); - // utils function describeBenchmark( diff --git a/tests/util/setup-dev-tests.ts b/tests/util/setup-dev-tests.ts index 94b1bf1b60b..1b91836a09b 100644 --- a/tests/util/setup-dev-tests.ts +++ b/tests/util/setup-dev-tests.ts @@ -17,7 +17,7 @@ import { providePolkadotApi, provideWeb3Api, } from "./providers"; -import { extractError, ExtrinsicCreation } from "./substrate-rpc"; +import { extractBatchError, extractError, ExtrinsicCreation } from "./substrate-rpc"; import type { BlockHash } from "@polkadot/types/interfaces/chain/types"; const debug = require("debug")("test:setup"); diff --git a/tests/util/substrate-rpc.ts b/tests/util/substrate-rpc.ts index 0acdee79710..5f588440982 100644 --- a/tests/util/substrate-rpc.ts +++ b/tests/util/substrate-rpc.ts @@ -150,7 +150,23 @@ function getDispatchInfo({ event: { data, method } }: EventRecord): DispatchInfo } export function extractError(events: EventRecord[] = []): DispatchError | undefined { - return filterAndApply(events, "system", ["ExtrinsicFailed"], getDispatchError)[0]; + return ( + filterAndApply(events, "system", ["ExtrinsicFailed"], getDispatchError)[0] || + extractBatchError(events) + ); +} + +export function extractBatchError(events: EventRecord[] = []): DispatchError | undefined { + return filterAndApply( + events, + "utility", + ["BatchInterrupted"], + ({ + event: { + data: [_, error], + }, + }) => getDispatchError({ event: { data: [error] } } as EventRecord) + )[0]; } export function isExtrinsicSuccessful(events: EventRecord[] = []): boolean {