#![cfg(test)]
mod mock;
use frame_support::{
assert_noop, assert_ok, hypothetically,
traits::{fungible::InspectHold, Currency},
};
use mock::*;
use pallet_nomination_pools::{
BondExtra, BondedPools, CommissionChangeRate, ConfigOp, Error as PoolsError,
Event as PoolsEvent, LastPoolId, PoolMember, PoolMembers, PoolState,
};
use pallet_staking::{
CurrentEra, Error as StakingError, Event as StakingEvent, Payee, RewardDestination,
};
use pallet_delegated_staking::Event as DelegatedStakingEvent;
use sp_runtime::{bounded_btree_map, traits::Zero, Perbill};
use sp_staking::Agent;
#[test]
fn pool_lifecycle_e2e() {
new_test_ext().execute_with(|| {
assert_eq!(Balances::minimum_balance(), 5);
assert_eq!(CurrentEra::<T>::get(), None);
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
assert_eq!(LastPoolId::<Runtime>::get(), 1);
assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true },
]
);
assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1));
assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },
PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true },
]
);
assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying));
assert_noop!(
Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
PoolsError::<Runtime>::MinimumBondNotMet,
);
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras.len(), 1);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 0);
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().unbonding_eras.len(), 1);
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().points, 0);
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying },
PoolsEvent::Unbonded { member: 20, pool_id: 1, points: 10, balance: 10, era: 3 },
PoolsEvent::Unbonded { member: 21, pool_id: 1, points: 10, balance: 10, era: 3 },
]
);
assert_noop!(
Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
PoolsError::<Runtime>::MinimumBondNotMet,
);
for e in 1..BondingDuration::get() {
CurrentEra::<Runtime>::set(Some(e));
assert_noop!(
Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0),
PoolsError::<Runtime>::CannotWithdrawAny
);
}
CurrentEra::<Runtime>::set(Some(BondingDuration::get()));
assert_noop!(
Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
PoolsError::<Runtime>::MinimumBondNotMet,
);
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0));
assert!(PoolMembers::<Runtime>::get(20).is_none());
assert!(PoolMembers::<Runtime>::get(21).is_none());
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 },]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 },
PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 },
PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 },
PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 },
]
);
assert_noop!(
Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
pallet_staking::Error::<Runtime>::InsufficientBond
);
assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 50));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Chilled { stash: POOL1_BONDED },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 50 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 50, balance: 50, era: 6 }]
);
CurrentEra::<Runtime>::set(Some(BondingDuration::get() * 2));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 1));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 50 },]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 },
PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 },
PoolsEvent::Destroyed { pool_id: 1 }
]
);
})
}
#[test]
fn pool_chill_e2e() {
new_test_ext().execute_with(|| {
assert_eq!(Balances::minimum_balance(), 5);
assert_eq!(CurrentEra::<T>::get(), None);
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
assert_eq!(LastPoolId::<Runtime>::get(), 1);
assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true },
]
);
assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1));
assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },
PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true },
]
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
pallet_staking::ConfigOp::Set(55), pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10),);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras.len(), 1);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 0);
assert_noop!(
Pools::unbond(RuntimeOrigin::signed(21), 21, 10),
StakingError::<Runtime>::InsufficientBond,
);
assert_ok!(Pools::chill(RuntimeOrigin::signed(20), 1));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().unbonding_eras.len(), 1);
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().points, 0);
assert_noop!(
Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]),
PoolsError::<Runtime>::MinimumBondNotMet,
);
assert_ok!(Pools::join(RuntimeOrigin::signed(22), 10, 1));
assert_noop!(
Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]),
PoolsError::<Runtime>::MinimumBondNotMet,
);
assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10)));
assert_noop!(
Pools::chill(RuntimeOrigin::signed(20), 1),
PoolsError::<Runtime>::NotNominator,
);
assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
CurrentEra::<Runtime>::set(Some(BondingDuration::get()));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Chilled { stash: POOL1_BONDED },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 },
]
);
})
}
#[test]
fn pool_slash_e2e() {
new_test_ext().execute_with(|| {
ExistentialDeposit::set(1);
assert_eq!(Balances::minimum_balance(), 1);
assert_eq!(CurrentEra::<T>::get(), None);
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10));
assert_eq!(LastPoolId::<Runtime>::get(), 1);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
]
);
assert_eq!(
Payee::<Runtime>::get(POOL1_BONDED),
Some(RewardDestination::Account(POOL1_REWARD))
);
assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1));
assert_ok!(Pools::join(RuntimeOrigin::signed(21), 20, 1));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 }
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true },
PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 20, joined: true },
]
);
CurrentEra::<Runtime>::set(Some(1));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 10));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 4 },
PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 4 }
]
);
CurrentEra::<Runtime>::set(Some(2));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 10));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 5 },
PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 5 },
PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 10, points: 10, era: 5 },
]
);
pallet_staking::slashing::do_slash::<Runtime>(
&POOL1_BONDED,
30,
&mut Default::default(),
&mut Default::default(),
2, );
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 5, balance: 15 },
PoolsEvent::PoolSlashed { pool_id: 1, balance: 15 }
]
);
CurrentEra::<Runtime>::set(Some(3));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
assert_eq!(
PoolMembers::<Runtime>::get(21).unwrap(),
PoolMember {
pool_id: 1,
points: 0,
last_recorded_reward_counter: Zero::zero(),
unbonding_eras: bounded_btree_map!(5 => 10, 6 => 5)
}
);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 5, points: 5, era: 6 }]
);
CurrentEra::<Runtime>::set(Some(6));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0));
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 10 + 5, points: 20 },
PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 },
PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 5 + 5, points: 15 },
PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 }
]
);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 15 + 10 + 15 }]
);
assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 20));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying },
PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 10, balance: 10, era: 9 }
]
);
CurrentEra::<Runtime>::set(Some(9));
assert_eq!(
PoolMembers::<Runtime>::get(10).unwrap(),
PoolMember {
pool_id: 1,
points: 0,
last_recorded_reward_counter: Zero::zero(),
unbonding_eras: bounded_btree_map!(4 => 10, 5 => 10, 9 => 10)
}
);
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 10 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Withdrawn { member: 10, pool_id: 1, balance: 10 + 15, points: 30 },
PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 },
PoolsEvent::Destroyed { pool_id: 1 }
]
);
});
}
#[test]
fn pool_slash_proportional() {
new_test_ext().execute_with(|| {
ExistentialDeposit::set(2);
BondingDuration::set(28);
assert_eq!(Balances::minimum_balance(), 2);
assert_eq!(Staking::current_era(), None);
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10));
assert_eq!(LastPoolId::<T>::get(), 1);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: 10,
amount: 40
}]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
]
);
let bond = 20;
assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1));
assert_ok!(Pools::join(RuntimeOrigin::signed(21), bond, 1));
assert_ok!(Pools::join(RuntimeOrigin::signed(22), bond, 1));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond },
]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![
DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: 20,
amount: bond
},
DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: 21,
amount: bond
},
DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: 22,
amount: bond
}
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true },
PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: bond, joined: true },
PoolsEvent::Bonded { member: 22, pool_id: 1, bonded: bond, joined: true },
]
);
CurrentEra::<T>::set(Some(99));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded {
member: 20,
pool_id: 1,
balance: bond,
points: bond,
era: 127
}]
);
CurrentEra::<T>::set(Some(100));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, bond));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded {
member: 21,
pool_id: 1,
balance: bond,
points: bond,
era: 128
}]
);
CurrentEra::<T>::set(Some(101));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(22), 22, bond));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded {
member: 22,
pool_id: 1,
balance: bond,
points: bond,
era: 129
}]
);
assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40);
assert_eq!(Pools::api_pool_pending_slash(1), 0);
assert_noop!(
Pools::apply_slash(RuntimeOrigin::signed(10), 21),
PoolsError::<Runtime>::NothingToSlash
);
hypothetically!({
pallet_staking::slashing::do_slash::<Runtime>(
&POOL1_BONDED,
3,
&mut Default::default(),
&mut Default::default(),
100,
);
assert_eq!(Pools::api_pool_pending_slash(1), 3);
assert_eq!(Pools::api_member_pending_slash(21), 1);
assert_noop!(
Pools::apply_slash(RuntimeOrigin::signed(10), 21),
PoolsError::<Runtime>::SlashTooLow
);
});
pallet_staking::slashing::do_slash::<Runtime>(
&POOL1_BONDED,
50,
&mut Default::default(),
&mut Default::default(),
100,
);
assert_eq!(Pools::api_pool_pending_slash(1), 50);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 50 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 128, balance: 7 },
PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 129, balance: 8 },
PoolsEvent::PoolSlashed { pool_id: 1, balance: 15 }
]
);
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().total_balance(), 7);
assert_eq!(Balances::total_balance_on_hold(&21), bond);
assert_eq!(Pools::api_member_pending_slash(21), bond - 7);
assert_ok!(Pools::apply_slash(RuntimeOrigin::signed(10), 21));
assert_eq!(Balances::total_balance_on_hold(&21), 7);
assert_eq!(Pools::api_member_pending_slash(21), 0);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![DelegatedStakingEvent::Slashed {
agent: POOL1_BONDED,
delegator: 21,
amount: bond - 7
}]
);
assert_eq!(PoolMembers::<Runtime>::get(22).unwrap().total_balance(), 8);
assert_eq!(Balances::total_balance_on_hold(&22), bond);
CurrentEra::<T>::set(Some(129));
let pre_balance = Balances::free_balance(&22);
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(22), 22, 0));
assert_eq!(Balances::total_balance_on_hold(&22), 0);
assert_eq!(Balances::free_balance(&22), pre_balance + 8);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![
DelegatedStakingEvent::Slashed {
agent: POOL1_BONDED,
delegator: 22,
amount: bond - 8
},
DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: 22, amount: 8 },
]
);
});
}
#[test]
fn pool_slash_non_proportional_only_bonded_pool() {
new_test_ext().execute_with(|| {
ExistentialDeposit::set(1);
BondingDuration::set(28);
assert_eq!(Balances::minimum_balance(), 1);
assert_eq!(CurrentEra::<T>::get(), None);
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
]
);
let bond = 20;
assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true }]
);
CurrentEra::<T>::set(Some(99));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond }]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded {
member: 20,
pool_id: 1,
balance: bond,
points: bond,
era: 127
}]
);
CurrentEra::<T>::set(Some(100));
assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40);
pallet_staking::slashing::do_slash::<Runtime>(
&POOL1_BONDED,
30,
&mut Default::default(),
&mut Default::default(),
100,
);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::PoolSlashed { pool_id: 1, balance: 10 }]
);
});
}
#[test]
fn pool_slash_non_proportional_bonded_pool_and_chunks() {
new_test_ext().execute_with(|| {
ExistentialDeposit::set(1);
BondingDuration::set(28);
assert_eq!(Balances::minimum_balance(), 1);
assert_eq!(CurrentEra::<T>::get(), None);
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
]
);
let bond = 20;
assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true }]
);
CurrentEra::<T>::set(Some(99));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond }]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded {
member: 20,
pool_id: 1,
balance: bond,
points: bond,
era: 127
}]
);
CurrentEra::<T>::set(Some(100));
assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40);
pallet_staking::slashing::do_slash::<Runtime>(
&POOL1_BONDED,
50,
&mut Default::default(),
&mut Default::default(),
100,
);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 50 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 127, balance: 10 },
PoolsEvent::PoolSlashed { pool_id: 1, balance: 0 }
]
);
});
}
#[test]
fn pool_migration_e2e() {
new_test_ext().execute_with(|| {
LegacyAdapter::set(true);
assert_eq!(Balances::minimum_balance(), 5);
assert_eq!(CurrentEra::<T>::get(), None);
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
assert_eq!(LastPoolId::<Runtime>::get(), 1);
assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true },
]
);
let pre_20 = Balances::free_balance(20);
assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1));
let pre_21 = Balances::free_balance(21);
assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1));
let pre_22 = Balances::free_balance(22);
assert_ok!(Pools::join(RuntimeOrigin::signed(22), 10, 1));
assert_eq!(Balances::free_balance(20), pre_20 - 10);
assert_eq!(Balances::free_balance(21), pre_21 - 10);
assert_eq!(Balances::free_balance(22), pre_22 - 10);
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },
PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true },
PoolsEvent::Bonded { member: 22, pool_id: 1, bonded: 10, joined: true },
]
);
CurrentEra::<Runtime>::set(Some(2));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5));
CurrentEra::<Runtime>::set(Some(3));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 5, points: 5, era: 5 },
PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 10, points: 10, era: 6 },
]
);
assert!(!Pools::api_pool_needs_delegate_migration(1));
assert_noop!(
Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1),
PoolsError::<Runtime>::NotSupported
);
LegacyAdapter::set(false);
assert_noop!(
Pools::migrate_delegation(RuntimeOrigin::signed(10), 20),
PoolsError::<Runtime>::NotMigrated
);
assert!(Pools::api_pool_needs_delegate_migration(1));
assert_ok!(Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1));
assert!(!Pools::api_pool_needs_delegate_migration(1));
assert_noop!(
Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1),
PoolsError::<Runtime>::AlreadyMigrated
);
let proxy_delegator_1 =
DelegatedStaking::generate_proxy_delegator(Agent::from(POOL1_BONDED)).get();
assert_eq!(
delegated_staking_events_since_last_call(),
vec![DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: proxy_delegator_1,
amount: 50 + 10 * 3
}]
);
CurrentEra::<Runtime>::set(Some(5));
assert_noop!(
Pools::unbond(RuntimeOrigin::signed(22), 22, 5),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 10),
PoolsError::<Runtime>::NotMigrated
);
let pre_claim_balance_20 = Balances::total_balance(&20);
assert_eq!(Balances::total_balance_on_hold(&20), 0);
assert!(Pools::api_member_needs_delegate_migration(20));
assert_ok!(Pools::migrate_delegation(RuntimeOrigin::signed(10), 20));
assert_eq!(Balances::total_balance(&20), pre_claim_balance_20 + 10);
assert_eq!(Balances::total_balance_on_hold(&20), 10);
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 5));
assert_eq!(Balances::total_balance_on_hold(&20), 5);
assert_eq!(Balances::total_balance(&20), pre_claim_balance_20 + 10);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 5, points: 5 },]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![
DelegatedStakingEvent::MigratedDelegation {
agent: POOL1_BONDED,
delegator: 20,
amount: 10
},
DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: 20, amount: 5 }
]
);
let pre_migrate_balance_21 = Balances::total_balance(&21);
assert_eq!(Balances::total_balance_on_hold(&21), 0);
assert!(Pools::api_member_needs_delegate_migration(21));
assert_ok!(Pools::migrate_delegation(RuntimeOrigin::signed(10), 21));
assert_eq!(Balances::total_balance(&21), pre_migrate_balance_21 + 10);
assert_eq!(Balances::total_balance_on_hold(&21), 10);
assert_noop!(
Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 10),
PoolsError::<Runtime>::CannotWithdrawAny
);
CurrentEra::<Runtime>::set(Some(6));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 10));
assert_eq!(Balances::total_balance_on_hold(&21), 0);
assert_eq!(Balances::total_balance(&21), pre_migrate_balance_21 + 10);
assert_eq!(Balances::total_balance_on_hold(&22), 0);
let _ = Balances::make_free_balance_be(&22, 0);
assert!(Pools::api_member_needs_delegate_migration(22));
assert_ok!(Pools::migrate_delegation(RuntimeOrigin::signed(10), 22));
assert!(!Pools::api_member_needs_delegate_migration(22));
assert_noop!(
Pools::migrate_delegation(RuntimeOrigin::signed(10), 22),
PoolsError::<Runtime>::AlreadyMigrated
);
assert_eq!(Balances::total_balance(&22), 10);
assert_eq!(Balances::total_balance_on_hold(&22), 10);
assert_ok!(Pools::unbond(RuntimeOrigin::signed(22), 22, 5));
assert_noop!(
Pools::withdraw_unbonded(RuntimeOrigin::signed(22), 22, 5),
PoolsError::<Runtime>::CannotWithdrawAny
);
CurrentEra::<Runtime>::set(Some(9));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(22), 22, 10));
assert_eq!(Balances::total_balance_on_hold(&22), 10 - 5);
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 },
StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 }
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 10, points: 10 },
PoolsEvent::MemberRemoved { member: 21, pool_id: 1, released_balance: 0 },
PoolsEvent::Unbonded { member: 22, pool_id: 1, balance: 5, points: 5, era: 9 },
PoolsEvent::Withdrawn { member: 22, pool_id: 1, balance: 5, points: 5 },
]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![
DelegatedStakingEvent::MigratedDelegation {
agent: POOL1_BONDED,
delegator: 21,
amount: 10
},
DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: 21, amount: 10 },
DelegatedStakingEvent::MigratedDelegation {
agent: POOL1_BONDED,
delegator: 22,
amount: 10
},
DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: 22, amount: 5 }
]
);
})
}
#[test]
fn disable_pool_operations_on_non_migrated() {
new_test_ext().execute_with(|| {
LegacyAdapter::set(true);
assert_eq!(Balances::minimum_balance(), 5);
assert_eq!(CurrentEra::<T>::get(), None);
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
assert_eq!(LastPoolId::<Runtime>::get(), 1);
assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true },
]
);
let pre_20 = Balances::free_balance(20);
assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1));
assert_eq!(Balances::free_balance(20), pre_20 - 10);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },]
);
LegacyAdapter::set(false);
assert!(Pools::api_pool_needs_delegate_migration(1));
assert_noop!(
Pools::join(RuntimeOrigin::signed(21), 10, 1),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::pool_withdraw_unbonded(RuntimeOrigin::signed(10), 1, 0),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Blocked),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::set_metadata(RuntimeOrigin::signed(10), 1, vec![1, 1]),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::update_roles(
RuntimeOrigin::signed(10),
1,
ConfigOp::Set(5),
ConfigOp::Set(6),
ConfigOp::Set(7)
),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::chill(RuntimeOrigin::signed(10), 1),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::set_commission(RuntimeOrigin::signed(10), 1, None),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::set_commission_max(RuntimeOrigin::signed(10), 1, Zero::zero()),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::set_commission_change_rate(
RuntimeOrigin::signed(10),
1,
CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 }
),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::claim_commission(RuntimeOrigin::signed(10), 1),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::adjust_pool_deposit(RuntimeOrigin::signed(10), 1),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::set_commission_claim_permission(RuntimeOrigin::signed(10), 1, None),
PoolsError::<Runtime>::NotMigrated
);
assert_ok!(Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1));
assert_eq!(
delegated_staking_events_since_last_call(),
vec![DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: DelegatedStaking::generate_proxy_delegator(Agent::from(POOL1_BONDED))
.get(),
amount: 50 + 10
},]
);
assert!(Pools::api_member_needs_delegate_migration(20));
assert_noop!(
Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(5)),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::bond_extra_other(RuntimeOrigin::signed(10), 20, BondExtra::Rewards),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::claim_payout(RuntimeOrigin::signed(20)),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::unbond(RuntimeOrigin::signed(20), 20, 5),
PoolsError::<Runtime>::NotMigrated
);
assert_noop!(
Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0),
PoolsError::<Runtime>::NotMigrated
);
assert_ok!(Pools::migrate_delegation(RuntimeOrigin::signed(10), 20));
assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(5)));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 5 },]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 5, joined: false },]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![
DelegatedStakingEvent::MigratedDelegation {
agent: POOL1_BONDED,
delegator: 20,
amount: 10
},
DelegatedStakingEvent::Delegated { agent: POOL1_BONDED, delegator: 20, amount: 5 },
]
);
})
}
#[test]
fn pool_no_dangling_delegation() {
new_test_ext().execute_with(|| {
ExistentialDeposit::set(1);
assert_eq!(Balances::minimum_balance(), 1);
assert_eq!(CurrentEra::<T>::get(), None);
let alice = 10;
let bob = 20;
let charlie = 21;
assert_ok!(Pools::create(RuntimeOrigin::signed(alice), 40, alice, alice, alice));
assert_eq!(LastPoolId::<Runtime>::get(), 1);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: alice, pool_id: 1 },
PoolsEvent::Bonded { member: alice, pool_id: 1, bonded: 40, joined: true },
]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: alice,
amount: 40
},]
);
assert_eq!(
Payee::<Runtime>::get(POOL1_BONDED),
Some(RewardDestination::Account(POOL1_REWARD))
);
assert_ok!(Pools::join(RuntimeOrigin::signed(bob), 20, 1));
assert_ok!(Pools::join(RuntimeOrigin::signed(charlie), 20, 1));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 }
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Bonded { member: bob, pool_id: 1, bonded: 20, joined: true },
PoolsEvent::Bonded { member: charlie, pool_id: 1, bonded: 20, joined: true },
]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![
DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: bob,
amount: 20
},
DelegatedStakingEvent::Delegated {
agent: POOL1_BONDED,
delegator: charlie,
amount: 20
},
]
);
CurrentEra::<Runtime>::set(Some(1));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(bob), 20, 20));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 20 },]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded { member: bob, pool_id: 1, balance: 20, points: 20, era: 4 }]
);
CurrentEra::<Runtime>::set(Some(2));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(alice), 10, 10));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(charlie), 21, 10));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Unbonded { member: alice, pool_id: 1, balance: 10, points: 10, era: 5 },
PoolsEvent::Unbonded {
member: charlie,
pool_id: 1,
balance: 10,
points: 10,
era: 5
},
]
);
pallet_staking::slashing::do_slash::<Runtime>(
&POOL1_BONDED,
30,
&mut Default::default(),
&mut Default::default(),
2, );
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 5, balance: 10 },
PoolsEvent::PoolSlashed { pool_id: 1, balance: 20 } ]
);
assert_eq!(PoolMembers::<Runtime>::get(alice).unwrap().total_balance(), 20);
assert_eq!(PoolMembers::<Runtime>::get(bob).unwrap().total_balance(), 20);
assert_eq!(PoolMembers::<Runtime>::get(charlie).unwrap().total_balance(), 10);
assert_eq!(Pools::api_member_pending_slash(alice), 20);
assert_ok!(Pools::apply_slash(RuntimeOrigin::signed(10), alice));
assert_eq!(Pools::api_member_pending_slash(charlie), 10);
assert_ok!(Pools::apply_slash(RuntimeOrigin::signed(10), charlie));
assert_eq!(Pools::api_member_pending_slash(bob), 0);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![
DelegatedStakingEvent::Slashed {
agent: POOL1_BONDED,
delegator: alice,
amount: 20
},
DelegatedStakingEvent::Slashed {
agent: POOL1_BONDED,
delegator: charlie,
amount: 10
},
]
);
CurrentEra::<Runtime>::set(Some(15));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(charlie), charlie, 10));
assert_eq!(Balances::total_balance_on_hold(&alice), 20);
assert_eq!(PoolMembers::<Runtime>::get(alice).unwrap().total_balance(), 22);
assert_eq!(Balances::total_balance_on_hold(&charlie), 10);
assert_eq!(PoolMembers::<Runtime>::get(charlie).unwrap().total_balance(), 12);
assert_eq!(Balances::total_balance_on_hold(&bob), 20);
assert_eq!(PoolMembers::<Runtime>::get(bob).unwrap().total_balance(), 15);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![PoolsEvent::Unbonded {
member: charlie,
pool_id: 1,
balance: 5,
points: 5,
era: 18
}]
);
let bob_pre_withdraw_balance = Balances::free_balance(&bob);
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(bob), bob, 0));
assert_eq!(Balances::free_balance(&bob), bob_pre_withdraw_balance + 20);
assert_eq!(Balances::total_balance_on_hold(&bob), 0);
assert!(!PoolMembers::<Runtime>::contains_key(bob));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 30 },]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Withdrawn { pool_id: 1, member: bob, balance: 15, points: 20 },
PoolsEvent::MemberRemoved { pool_id: 1, member: bob, released_balance: 5 },
]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![
DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: bob, amount: 15 },
DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: bob, amount: 5 },
]
);
CurrentEra::<Runtime>::set(Some(18));
let charlie_pre_withdraw_balance = Balances::free_balance(&charlie);
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(charlie), charlie, 0));
assert_eq!(Balances::free_balance(&charlie), charlie_pre_withdraw_balance + 10);
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 },]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Withdrawn { pool_id: 1, member: charlie, balance: 10, points: 15 },
PoolsEvent::MemberRemoved { member: charlie, pool_id: 1, released_balance: 0 }
]
);
assert_eq!(
delegated_staking_events_since_last_call(),
vec![DelegatedStakingEvent::Released {
agent: POOL1_BONDED,
delegator: charlie,
amount: 10
},]
);
assert_ok!(Pools::set_state(RuntimeOrigin::signed(alice), 1, PoolState::Destroying));
assert_ok!(Pools::unbond(RuntimeOrigin::signed(alice), alice, 30));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 15 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying },
PoolsEvent::Unbonded {
member: alice,
pool_id: 1,
points: 15,
balance: 15,
era: 21
}
]
);
CurrentEra::<Runtime>::set(Some(21));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(alice), alice, 0));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 15 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Withdrawn { member: alice, pool_id: 1, balance: 20, points: 25 },
PoolsEvent::MemberRemoved { pool_id: 1, member: alice, released_balance: 0 },
PoolsEvent::Destroyed { pool_id: 1 }
]
);
assert_eq!(Balances::total_balance_on_hold(&alice), 0);
assert_eq!(Balances::total_balance_on_hold(&bob), 0);
assert_eq!(Balances::total_balance_on_hold(&charlie), 0);
});
}