use super::*;
use frame_support::{hypothetically_ok, traits::Currency};
#[test]
fn existing_stash_cannot_bond() {
ExtBuilder::default().build_and_execute(|| {
assert!(StakingLedger::<T>::is_bonded(11.into()));
assert_noop!(
Staking::bond(RuntimeOrigin::signed(11), 7, RewardDestination::Staked),
Error::<T>::AlreadyBonded,
);
});
}
#[test]
fn existing_controller_cannot_bond() {
ExtBuilder::default().build_and_execute(|| {
let (_stash, controller) = testing_utils::create_unique_stash_controller::<T>(
0,
7,
RewardDestination::Staked,
false,
)
.unwrap();
assert_noop!(
Staking::bond(RuntimeOrigin::signed(controller), 7, RewardDestination::Staked),
Error::<T>::AlreadyPaired,
);
});
}
#[test]
fn cannot_transfer_staked_balance() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert_eq!(asset::staked::<T>(&11), 1000);
assert_eq!(asset::total_balance::<T>(&11), 1000 + 1);
assert_eq!(asset::stakeable_balance::<T>(&11), 1000);
assert_eq!(asset::free_to_stake::<T>(&11), 0);
assert_noop!(
Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1),
TokenError::Frozen,
);
let _ = asset::set_stakeable_balance::<T>(&11, 10000);
assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1));
});
}
#[test]
fn cannot_reserve_staked_balance() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(asset::staked::<T>(&11), 1000);
assert_noop!(
Balances::reserve(&11, 2),
pallet_balances::Error::<T, _>::InsufficientBalance
);
assert_noop!(Balances::reserve(&11, 1), DispatchError::ConsumerRemaining);
let _ = asset::set_stakeable_balance::<T>(&11, 1000 + 1000);
assert_eq!(asset::free_to_stake::<T>(&11), 1000);
assert_ok!(Balances::reserve(&11, 500));
assert_eq!(asset::free_to_stake::<T>(&11), 500);
});
}
#[test]
fn cannot_bond_less_than_ed() {
ExtBuilder::default().existential_deposit(10).build_and_execute(|| {
assert_eq!(asset::staked_and_not::<T>(&1), (0, 10));
assert_noop!(
Staking::bond(RuntimeOrigin::signed(1), 9, RewardDestination::Staked),
Error::<T>::InsufficientBond,
);
assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 10, RewardDestination::Staked));
assert_eq!(asset::staked_and_not::<T>(&1), (10, 0));
});
}
#[test]
fn do_not_die_when_active_is_ed() {
let ed = 10;
ExtBuilder::default()
.existential_deposit(ed)
.balance_factor(ed)
.build_and_execute(|| {
assert_eq!(
Staking::ledger(21.into()).unwrap(),
StakingLedgerInspect {
stash: 21,
total: 1000 * ed,
active: 1000 * ed,
unlocking: Default::default(),
}
);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 999 * ed));
Session::roll_until_active_era(4);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(21), 0));
assert_eq!(
Staking::ledger(21.into()).unwrap(),
StakingLedgerInspect {
stash: 21,
total: ed,
active: ed,
unlocking: Default::default(),
}
);
})
}
#[test]
fn bond_truncated_to_maximum_possible() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(asset::free_to_stake::<T>(&1), 10);
assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 100, RewardDestination::Staked));
assert_eq!(Staking::ledger(1.into()).unwrap().total, 10);
});
}
#[test]
fn bond_extra_works() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
asset::set_stakeable_balance::<T>(&11, 1000000);
assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 100));
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000 + 100,
active: 1000 + 100,
unlocking: Default::default(),
}
);
assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), Balance::max_value()));
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000000,
active: 1000000,
unlocking: Default::default(),
}
);
});
}
#[test]
fn bond_extra_controller_bad_state_works() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
assert_eq!(StakingLedger::<T>::get(StakingAccount::Stash(31)).unwrap().stash, 31);
Bonded::<T>::insert(31, 41);
assert_eq!(Ledger::<T>::get(41).unwrap().stash, 41);
assert_noop!(Staking::bond_extra(RuntimeOrigin::signed(31), 10), Error::<T>::BadState);
})
}
#[test]
fn bond_extra_updates_exposure_later_if_exposed() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert_eq!(active_era(), 1);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
assert_eq!(
Staking::eras_stakers(active_era(), &11),
Exposure { total: 1000, own: 1000, others: vec![] }
);
asset::set_stakeable_balance::<T>(&11, 1000000);
Staking::bond_extra(RuntimeOrigin::signed(11), 100).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000 + 100,
active: 1000 + 100,
unlocking: Default::default(),
}
);
assert_ne!(
Staking::eras_stakers(active_era(), &11),
Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] }
);
Session::roll_until_active_era(2);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000 + 100,
active: 1000 + 100,
unlocking: Default::default(),
}
);
assert_eq!(
Staking::eras_stakers(active_era(), &11),
Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] }
);
})
}
#[test]
fn cannot_bond_extra_to_lower_than_ed() {
ExtBuilder::default()
.existential_deposit(11)
.balance_factor(11)
.build_and_execute(|| {
assert_eq!(
Staking::ledger(21.into()).unwrap(),
StakingLedgerInspect {
stash: 21,
total: 11 * 1000,
active: 11 * 1000,
unlocking: Default::default(),
}
);
assert_ok!(Staking::chill(RuntimeOrigin::signed(21)));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 11 * 1000));
assert_eq!(
Staking::ledger(21.into()).unwrap(),
StakingLedgerInspect {
stash: 21,
total: 11 * 1000,
active: 0,
unlocking: bounded_vec![UnlockChunk {
value: 11 * 1000,
era: active_era() + 3
}],
}
);
assert_noop!(
Staking::bond_extra(RuntimeOrigin::signed(21), 5),
Error::<Test>::InsufficientBond,
);
})
}
#[test]
fn unbonding_works() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 500).unwrap();
assert_eq!(
staking_events_since_last_call(),
vec![Event::Unbonded { stash: 11, amount: 500 }]
);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 500,
unlocking: bounded_vec![UnlockChunk { value: 500, era: active_era() + 3 }],
},
);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(staking_events_since_last_call(), vec![]);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 500,
unlocking: bounded_vec![UnlockChunk { value: 500, era: active_era() + 3 }],
},
);
Session::roll_until_active_era(2);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 500,
unlocking: bounded_vec![UnlockChunk { value: 500, era: 1 + 3 }],
},
);
Session::roll_until_active_era(3);
let _ = staking_events_since_last_call();
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(staking_events_since_last_call(), vec![]);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 500,
unlocking: bounded_vec![UnlockChunk { value: 500, era: 1 + 3 }],
},
);
Session::roll_until_active_era(4);
let _ = staking_events_since_last_call();
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(
staking_events_since_last_call(),
vec![Event::Withdrawn { stash: 11, amount: 500 }]
);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect { stash: 11, total: 500, active: 500, unlocking: bounded_vec![] },
);
});
}
#[test]
fn unbonding_multi_chunk() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 500).unwrap();
assert_eq!(
staking_events_since_last_call(),
vec![Event::Unbonded { stash: 11, amount: 500 }]
);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 500,
unlocking: bounded_vec![UnlockChunk { value: 500, era: active_era() + 3 }],
},
);
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
Staking::unbond(RuntimeOrigin::signed(11), 250).unwrap();
assert_eq!(
staking_events_since_last_call(),
vec![Event::Unbonded { stash: 11, amount: 250 }]
);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 250,
unlocking: bounded_vec![
UnlockChunk { value: 500, era: 1 + 3 },
UnlockChunk { value: 250, era: 2 + 3 }
],
},
);
Session::roll_until_active_era(4);
let _ = staking_events_since_last_call();
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(
staking_events_since_last_call(),
vec![Event::Withdrawn { stash: 11, amount: 500 }]
);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 500,
active: 250,
unlocking: bounded_vec![UnlockChunk { value: 250, era: 2 + 3 }],
},
);
Session::roll_until_active_era(5);
let _ = staking_events_since_last_call();
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(
staking_events_since_last_call(),
vec![Event::Withdrawn { stash: 11, amount: 250 }]
);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect { stash: 11, total: 250, active: 250, unlocking: bounded_vec![] },
);
});
}
#[test]
fn full_unbonding_works() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(asset::free_to_stake::<T>(&11), 0);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1000));
assert_eq!(
staking_events_since_last_call(),
vec![Event::Chilled { stash: 11 }, Event::Unbonded { stash: 11, amount: 1000 }]
);
Session::roll_until_active_era(active_era() + 3);
let _ = staking_events_since_last_call();
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(
staking_events_since_last_call(),
vec![Event::StakerRemoved { stash: 11 }, Event::Withdrawn { stash: 11, amount: 1000 }]
);
StakingLedger::<T>::assert_stash_killed(11);
assert_eq!(asset::free_to_stake::<T>(&11), 1000);
});
}
#[test]
fn unbond_with_chill_works() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Stash));
let _ = asset::stakeable_balance::<T>(&11);
asset::set_stakeable_balance::<T>(&11, 1000000);
assert!(Validators::<T>::contains_key(11));
let initial_validator_count = Validators::<T>::count();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default()
}
);
Session::roll_until_active_era(2);
assert_eq!(active_era(), 2);
let _ = staking_events_since_last_call();
assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1000));
assert_eq!(
staking_events_since_last_call(),
vec![Event::Chilled { stash: 11 }, Event::Unbonded { stash: 11, amount: 1000 }]
);
assert!(!Validators::<T>::contains_key(11));
assert!(Nominators::<T>::get(11).is_none());
assert_eq!(Validators::<T>::count(), initial_validator_count - 1);
});
}
#[test]
fn unbonding_merges_if_era_exists() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 500).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 500,
unlocking: bounded_vec![UnlockChunk { value: 500, era: 1 + 3 }],
},
);
Staking::unbond(RuntimeOrigin::signed(11), 250).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 250,
unlocking: bounded_vec![UnlockChunk { value: 500 + 250, era: 1 + 3 }],
},
);
});
}
#[test]
fn unbonding_rejects_if_max_chunks() {
ExtBuilder::default()
.max_unlock_chunks(3)
.bonding_duration(7)
.build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 250).unwrap();
Session::roll_until_active_era(2);
Staking::unbond(RuntimeOrigin::signed(11), 250).unwrap();
Session::roll_until_active_era(3);
Staking::unbond(RuntimeOrigin::signed(11), 250).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 250,
unlocking: bounded_vec![
UnlockChunk { value: 250, era: 1 + 7 },
UnlockChunk { value: 250, era: 2 + 7 },
UnlockChunk { value: 250, era: 3 + 7 },
],
},
);
Session::roll_until_active_era(4);
assert_noop!(Staking::unbond(RuntimeOrigin::signed(11), 100), Error::<T>::NoMoreChunks,);
});
}
#[test]
fn unbonding_auto_withdraws_if_any() {
ExtBuilder::default().max_unlock_chunks(3).build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 250).unwrap();
Session::roll_until_active_era(2);
Staking::unbond(RuntimeOrigin::signed(11), 250).unwrap();
Session::roll_until_active_era(3);
Staking::unbond(RuntimeOrigin::signed(11), 250).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 250,
unlocking: bounded_vec![
UnlockChunk { value: 250, era: 1 + 3 },
UnlockChunk { value: 250, era: 2 + 3 },
UnlockChunk { value: 250, era: 3 + 3 },
],
},
);
Session::roll_until_active_era(4);
Staking::unbond(RuntimeOrigin::signed(11), 100).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 750,
active: 150,
unlocking: bounded_vec![
UnlockChunk { value: 250, era: 2 + 3 },
UnlockChunk { value: 250, era: 3 + 3 },
UnlockChunk { value: 100, era: 4 + 3 },
],
},
);
});
}
#[test]
fn unbonding_caps_to_ledger_active() {
ExtBuilder::default()
.nominate(false)
.set_status(11, StakerStatus::Idle)
.build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 1500).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 0,
unlocking: bounded_vec![UnlockChunk { value: 1000, era: 1 + 3 }],
}
);
});
}
#[test]
fn unbond_avoids_dust() {
ExtBuilder::default()
.existential_deposit(5)
.nominate(false)
.set_status(11, StakerStatus::Idle)
.build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 998).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 0,
unlocking: bounded_vec![UnlockChunk { value: 1000, era: 1 + 3 }],
}
);
});
}
#[test]
fn unbond_rejects_if_min_role_bond_not_met() {
ExtBuilder::default().min_validator_bond(100).build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
assert_noop!(Staking::unbond(RuntimeOrigin::signed(11), 950), Error::<T>::InsufficientBond);
hypothetically_ok!(Staking::unbond(RuntimeOrigin::signed(11), 850));
hypothetically!({
assert_ok!(Staking::chill(RuntimeOrigin::signed(11)));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 950));
})
})
}
#[test]
fn reducing_max_unlocking_chunks_abrupt() {
ExtBuilder::default().build_and_execute(|| {
MaxUnlockingChunks::set(2);
Session::roll_until_active_era(10);
assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 300, RewardDestination::Staked));
assert!(matches!(Staking::ledger(3.into()), Ok(_)));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 20));
let expected_unlocking: BoundedVec<UnlockChunk<Balance>, MaxUnlockingChunks> =
bounded_vec![UnlockChunk { value: 20 as Balance, era: 13 as EraIndex }];
assert!(matches!(Staking::ledger(3.into()),
Ok(StakingLedger {
unlocking,
..
}) if unlocking == expected_unlocking));
Session::roll_until_active_era(11);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 50));
let expected_unlocking: BoundedVec<UnlockChunk<Balance>, MaxUnlockingChunks> =
bounded_vec![UnlockChunk { value: 20, era: 13 }, UnlockChunk { value: 50, era: 14 }];
assert!(matches!(Staking::ledger(3.into()),
Ok(StakingLedger {
unlocking,
..
}) if unlocking == expected_unlocking));
Session::roll_until_active_era(12);
assert_noop!(Staking::unbond(RuntimeOrigin::signed(3), 20), Error::<Test>::NoMoreChunks);
MaxUnlockingChunks::set(1);
assert_noop!(Staking::unbond(RuntimeOrigin::signed(3), 20), Error::<Test>::NotController);
assert_noop!(Staking::rebond(RuntimeOrigin::signed(3), 100), Error::<Test>::NotController);
MaxUnlockingChunks::set(2);
assert_ok!(Staking::rebond(RuntimeOrigin::signed(3), 20));
})
}
#[test]
fn switching_roles() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
for i in &[11, 21] {
assert_ok!(Staking::set_payee(RuntimeOrigin::signed(*i), RewardDestination::Stash));
}
assert_eq_uvec!(Session::validators(), vec![21, 11]);
for i in 1..7 {
let _ = Balances::deposit_creating(&i, 5000);
}
assert_ok!(Staking::bond(RuntimeOrigin::signed(5), 1000, RewardDestination::Account(5)));
assert_ok!(Staking::validate(RuntimeOrigin::signed(5), ValidatorPrefs::default()));
assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 2000, RewardDestination::Account(1)));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(1), vec![11, 5]));
assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 500, RewardDestination::Account(3)));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![21]));
Session::roll_until_active_era(2);
assert_eq_uvec!(Session::validators(), vec![5, 11]);
assert_ok!(Staking::validate(RuntimeOrigin::signed(1), ValidatorPrefs::default()));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![21, 1]));
Session::roll_until_active_era(3);
assert_eq_uvec!(Session::validators(), vec![1, 21]);
});
}
#[test]
fn bond_with_no_staked_value() {
ExtBuilder::default()
.validator_count(3)
.existential_deposit(5)
.balance_factor(5)
.nominate(false)
.build_and_execute(|| {
assert_noop!(
Staking::bond(RuntimeOrigin::signed(1), 1, RewardDestination::Account(1)),
Error::<Test>::InsufficientBond,
);
assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 5, RewardDestination::Account(1)));
assert_eq!(pallet_balances::Holds::<Test>::get(&1)[0].amount, 5);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(1), 1));
assert_eq!(
Staking::ledger(1.into()).unwrap(),
StakingLedgerInspect {
stash: 1,
active: 0,
total: 5,
unlocking: bounded_vec![UnlockChunk { value: 5, era: 4 }],
}
);
Session::roll_until_active_era(2);
Session::roll_until_active_era(3);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0));
assert!(Staking::ledger(1.into()).is_ok());
assert_eq!(pallet_balances::Holds::<Test>::get(&1)[0].amount, 5);
Session::roll_until_active_era(4);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0));
assert!(Staking::ledger(1.into()).is_err());
assert_eq!(pallet_balances::Holds::<Test>::get(&1).len(), 0);
});
}
#[test]
fn bond_with_little_staked_value_bounded() {
ExtBuilder::default().validator_count(3).nominate(false).build_and_execute(|| {
assert_ok!(Staking::chill(RuntimeOrigin::signed(31)));
assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Stash));
assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 1, RewardDestination::Account(1)));
assert_ok!(Staking::validate(RuntimeOrigin::signed(1), ValidatorPrefs::default()));
reward_all_elected();
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
mock::make_all_reward_payment(1);
assert_eq_uvec!(session_validators(), vec![21, 11, 1]);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::PayoutStarted { era_index: 1, validator_stash: 11, page: 0, next: None },
Event::Rewarded { stash: 11, dest: RewardDestination::Stash, amount: 2500 },
Event::PayoutStarted { era_index: 1, validator_stash: 21, page: 0, next: None },
Event::Rewarded { stash: 21, dest: RewardDestination::Staked, amount: 2500 },
Event::PayoutStarted { era_index: 1, validator_stash: 31, page: 0, next: None },
Event::Rewarded { stash: 31, dest: RewardDestination::Staked, amount: 2500 }
]
);
reward_all_elected();
Session::roll_until_active_era(3);
let _ = staking_events_since_last_call();
mock::make_all_reward_payment(2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::PayoutStarted { era_index: 2, validator_stash: 1, page: 0, next: None },
Event::Rewarded { stash: 1, dest: RewardDestination::Account(1), amount: 2500 },
Event::PayoutStarted { era_index: 2, validator_stash: 11, page: 0, next: None },
Event::Rewarded { stash: 11, dest: RewardDestination::Stash, amount: 2500 },
Event::PayoutStarted { era_index: 2, validator_stash: 21, page: 0, next: None },
Event::Rewarded { stash: 21, dest: RewardDestination::Staked, amount: 2500 }
]
);
assert_eq_uvec!(session_validators(), vec![21, 11, 1]);
assert_eq!(Staking::eras_stakers(active_era(), &1).total, 1);
});
}
#[test]
fn restricted_accounts_can_only_withdraw() {
ExtBuilder::default().build_and_execute(|| {
let alice = 301;
let _ = Balances::make_free_balance_be(&alice, 500);
assert_ok!(Staking::bond(RuntimeOrigin::signed(alice), 100, RewardDestination::Staked));
let bob = 302;
let _ = Balances::make_free_balance_be(&bob, 500);
restrict(&bob);
assert_noop!(
Staking::bond(RuntimeOrigin::signed(bob), 100, RewardDestination::Staked,),
Error::<Test>::Restricted
);
restrict(&alice);
assert_noop!(
Staking::bond_extra(RuntimeOrigin::signed(alice), 100),
Error::<Test>::Restricted
);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(alice), 100));
Session::roll_until_active_era(2);
assert_noop!(Staking::rebond(RuntimeOrigin::signed(alice), 50), Error::<Test>::Restricted);
Session::roll_until_active_era(5);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(alice), 0));
assert_noop!(
Staking::bond(RuntimeOrigin::signed(alice), 100, RewardDestination::Staked,),
Error::<Test>::Restricted
);
remove_from_restrict_list(&bob);
assert_ok!(Staking::bond(RuntimeOrigin::signed(bob), 100, RewardDestination::Staked));
assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(bob), 100));
Session::roll_until_active_era(6);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(bob), 100));
Session::roll_until_active_era(6);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(bob), 0));
})
}
mod rebond {
use super::*;
#[test]
fn rebond_works() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
assert_noop!(
Staking::rebond(RuntimeOrigin::signed(11), 500),
Error::<Test>::NoUnlockChunk
);
Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 100,
unlocking: bounded_vec![UnlockChunk { value: 900, era: 1 + 3 }],
}
);
Staking::rebond(RuntimeOrigin::signed(11), 900).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 100,
unlocking: bounded_vec![UnlockChunk { value: 900, era: 1 + 3 }],
}
);
Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 600,
unlocking: bounded_vec![UnlockChunk { value: 400, era: 1 + 3 }],
}
);
Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap();
Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap();
Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 100,
unlocking: bounded_vec![UnlockChunk { value: 900, era: 1 + 3 }],
}
);
Staking::rebond(RuntimeOrigin::signed(11), 500).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 600,
unlocking: bounded_vec![UnlockChunk { value: 400, era: 1 + 3 }],
}
);
})
}
#[test]
fn rebond_is_fifo() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Staking::unbond(RuntimeOrigin::signed(11), 400).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 600,
unlocking: bounded_vec![UnlockChunk { value: 400, era: 1 + 3 }],
}
);
Session::roll_until_active_era(2);
Staking::unbond(RuntimeOrigin::signed(11), 300).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 300,
unlocking: bounded_vec![
UnlockChunk { value: 400, era: 1 + 3 },
UnlockChunk { value: 300, era: 2 + 3 },
],
}
);
Session::roll_until_active_era(3);
Staking::unbond(RuntimeOrigin::signed(11), 200).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 100,
unlocking: bounded_vec![
UnlockChunk { value: 400, era: 1 + 3 },
UnlockChunk { value: 300, era: 2 + 3 },
UnlockChunk { value: 200, era: 3 + 3 },
],
}
);
Staking::rebond(RuntimeOrigin::signed(11), 400).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 500,
unlocking: bounded_vec![
UnlockChunk { value: 400, era: 1 + 3 },
UnlockChunk { value: 100, era: 2 + 3 },
],
}
);
})
}
#[test]
fn rebond_emits_right_value_in_event() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
let _ = asset::set_stakeable_balance::<Test>(&11, 1000000);
Staking::unbond(RuntimeOrigin::signed(11), 900).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 100,
unlocking: bounded_vec![UnlockChunk { value: 900, era: 1 + 3 }],
}
);
assert_eq!(
staking_events_since_last_call(),
vec![Event::Unbonded { stash: 11, amount: 900 }]
);
Staking::rebond(RuntimeOrigin::signed(11), 100).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 200,
unlocking: bounded_vec![UnlockChunk { value: 800, era: 1 + 3 }],
}
);
assert_eq!(
staking_events_since_last_call(),
vec![Event::Bonded { stash: 11, amount: 100 }]
);
Staking::rebond(RuntimeOrigin::signed(11), 100_000).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
assert_eq!(
staking_events_since_last_call(),
vec![Event::Bonded { stash: 11, amount: 800 }]
);
});
}
#[test]
fn cannot_rebond_to_lower_than_ed() {
ExtBuilder::default()
.existential_deposit(11)
.balance_factor(11)
.build_and_execute(|| {
assert_eq!(
Staking::ledger(21.into()).unwrap(),
StakingLedgerInspect {
stash: 21,
total: 11 * 1000,
active: 11 * 1000,
unlocking: Default::default(),
}
);
assert_ok!(Staking::chill(RuntimeOrigin::signed(21)));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 11 * 1000));
assert_eq!(
Staking::ledger(21.into()).unwrap(),
StakingLedgerInspect {
stash: 21,
total: 11 * 1000,
active: 0,
unlocking: bounded_vec![UnlockChunk { value: 11 * 1000, era: 4 }],
}
);
assert_noop!(
Staking::rebond(RuntimeOrigin::signed(21), 5),
Error::<Test>::InsufficientBond
);
})
}
}
mod reap {
use super::*;
#[test]
fn reap_stash_works() {
ExtBuilder::default()
.min_nominator_bond(1_000)
.min_validator_bond(1_500)
.existential_deposit(10)
.balance_factor(10)
.build_and_execute(|| {
assert_eq!(asset::staked::<Test>(&11), 10 * 1000);
assert_eq!(Staking::bonded(&11), Some(11));
assert!(<Ledger<Test>>::contains_key(&11));
assert!(<Bonded<Test>>::contains_key(&11));
assert!(<Validators<Test>>::contains_key(&11));
assert!(<Payee<Test>>::contains_key(&11));
assert_noop!(
Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0),
Error::<Test>::FundedTarget
);
Ledger::<Test>::insert(11, StakingLedger::<Test>::new(11, 1499));
assert_noop!(
Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0),
Error::<Test>::FundedTarget
);
Ledger::<Test>::insert(11, StakingLedger::<Test>::new(11, 9));
assert_ok!(Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0));
assert!(!<Ledger<Test>>::contains_key(&11));
assert!(!<Bonded<Test>>::contains_key(&11));
assert!(!<Validators<Test>>::contains_key(&11));
assert!(!<Payee<Test>>::contains_key(&11));
assert_eq!(asset::staked::<Test>(&11), 0);
});
}
#[test]
fn reap_stash_works_with_existential_deposit_zero() {
ExtBuilder::default()
.existential_deposit(0)
.balance_factor(10)
.build_and_execute(|| {
assert_eq!(asset::staked::<Test>(&11), 10 * 1000);
assert_eq!(Staking::bonded(&11), Some(11));
assert!(<Ledger<Test>>::contains_key(&11));
assert!(<Bonded<Test>>::contains_key(&11));
assert!(<Validators<Test>>::contains_key(&11));
assert!(<Payee<Test>>::contains_key(&11));
assert_noop!(
Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0),
Error::<Test>::FundedTarget
);
Ledger::<Test>::insert(11, StakingLedger::<Test>::new(11, 0));
assert_ok!(Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0));
assert!(!<Ledger<Test>>::contains_key(&11));
assert!(!<Bonded<Test>>::contains_key(&11));
assert!(!<Validators<Test>>::contains_key(&11));
assert!(!<Payee<Test>>::contains_key(&11));
assert_eq!(asset::staked::<Test>(&11), 0);
});
}
}
mod nominate {
use super::*;
#[test]
fn duplicate_nominations_stripped() {
ExtBuilder::default().nominate(false).set_stake(31, 1000).build_and_execute(|| {
assert_eq!(
<Validators<Test>>::iter()
.map(|(v, _)| (v, Staking::ledger(v.into()).unwrap().total))
.collect::<Vec<_>>(),
vec![(31, 1000), (21, 1000), (11, 1000)],
);
assert!(<Nominators<T>>::iter().map(|(n, _)| n).collect::<Vec<_>>().is_empty());
bond_nominator(1, 1000, vec![11, 11, 11, 21, 31]);
assert_eq!(
Nominators::<T>::get(1).unwrap(),
Nominations {
targets: bounded_vec![11, 21, 31],
submitted_in: 1,
suppressed: false
}
);
});
}
#[test]
fn nominating_non_validators_is_not_ok() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert_eq!(
<Validators<Test>>::iter().map(|(v, _)| v).collect::<Vec<_>>(),
vec![31, 21, 11,],
);
assert!(<Nominators<T>>::iter().count() == 0);
assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 1000, RewardDestination::Stash));
assert_noop!(
Staking::nominate(RuntimeOrigin::signed(1), vec![41]),
Error::<Test>::BadTarget
);
assert_noop!(
Staking::nominate(RuntimeOrigin::signed(1), vec![31, 21, 11, 41]),
Error::<Test>::BadTarget
);
});
}
#[test]
fn blocking_and_kicking_works() {
ExtBuilder::default().validator_count(4).nominate(true).build_and_execute(|| {
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { blocked: true, ..Default::default() }
));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), vec![11]));
assert_eq!(Nominators::<Test>::get(&101).unwrap().targets, vec![11]);
assert_ok!(Staking::kick(RuntimeOrigin::signed(11), vec![101]));
assert!(Nominators::<Test>::get(&101).unwrap().targets.is_empty());
assert_noop!(
Staking::nominate(RuntimeOrigin::signed(101), vec![11]),
Error::<Test>::BadTarget
);
});
}
}
mod staking_bounds_chill_other {
use super::*;
#[test]
fn min_bond_checks_work() {
ExtBuilder::default()
.existential_deposit(100)
.balance_factor(100)
.min_nominator_bond(1_000)
.min_validator_bond(1_500)
.build_and_execute(|| {
assert_noop!(
Staking::bond(RuntimeOrigin::signed(3), 50, RewardDestination::Stash),
Error::<Test>::InsufficientBond
);
assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1000, RewardDestination::Stash));
assert_noop!(
Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()),
Error::<Test>::InsufficientBond,
);
assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![11]));
assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(3), 500));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![11]));
assert_ok!(Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()));
assert_noop!(
Staking::unbond(RuntimeOrigin::signed(3), 500),
Error::<Test>::InsufficientBond
);
assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![11]));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 500));
assert_noop!(
Staking::unbond(RuntimeOrigin::signed(3), 500),
Error::<Test>::InsufficientBond
);
assert_ok!(Staking::chill(RuntimeOrigin::signed(3)));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 1000));
})
}
#[test]
fn chill_other_works() {
ExtBuilder::default()
.existential_deposit(100)
.balance_factor(100)
.min_nominator_bond(1_000)
.min_validator_bond(1_500)
.build_and_execute(|| {
let initial_validators = Validators::<Test>::count();
let initial_nominators = Nominators::<Test>::count();
for i in 0..15 {
let a = 4 * i;
let b = 4 * i + 2;
asset::set_stakeable_balance::<Test>(&a, 100_000);
asset::set_stakeable_balance::<Test>(&b, 100_000);
assert_ok!(Staking::bond(
RuntimeOrigin::signed(a),
1000,
RewardDestination::Stash
));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(a), vec![11]));
assert_ok!(Staking::bond(
RuntimeOrigin::signed(b),
1500,
RewardDestination::Stash
));
assert_ok!(Staking::validate(
RuntimeOrigin::signed(b),
ValidatorPrefs::default()
));
assert_eq!(
staking_events_since_last_call(),
vec![
Event::Bonded { stash: a, amount: 1000 },
Event::Bonded { stash: b, amount: 1500 },
Event::ValidatorPrefsSet {
stash: b,
prefs: ValidatorPrefs { commission: Zero::zero(), blocked: false }
}
]
);
}
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Noop,
));
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Set(1_500),
ConfigOp::Set(2_000),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
));
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Set(10),
ConfigOp::Set(10),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
));
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Set(Percent::from_percent(75)),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
));
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Set(1_500),
ConfigOp::Set(2_000),
ConfigOp::Set(10),
ConfigOp::Set(10),
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Noop,
));
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Set(Percent::from_percent(75)),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
));
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Set(10),
ConfigOp::Set(10),
ConfigOp::Set(Percent::from_percent(75)),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
));
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Set(1_500),
ConfigOp::Set(2_000),
ConfigOp::Set(10),
ConfigOp::Set(10),
ConfigOp::Set(Percent::from_percent(75)),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
));
assert_eq!(Nominators::<Test>::count(), 15 + initial_nominators);
assert_eq!(Validators::<Test>::count(), 15 + initial_validators);
for i in 6..15 {
let b = 4 * i;
let d = 4 * i + 2;
assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), b));
assert_eq!(*staking_events().last().unwrap(), Event::Chilled { stash: b });
assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), d));
assert_eq!(*staking_events().last().unwrap(), Event::Chilled { stash: d });
}
assert_eq!(Nominators::<Test>::count(), 7);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_eq!(Validators::<Test>::count(), 9);
assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), 2));
})
}
#[test]
fn capped_stakers_works() {
ExtBuilder::default().build_and_execute(|| {
let validator_count = Validators::<Test>::count();
assert_eq!(validator_count, 3);
let nominator_count = Nominators::<Test>::count();
assert_eq!(nominator_count, 1);
let max = 10;
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Set(10),
ConfigOp::Set(10),
ConfigOp::Set(max),
ConfigOp::Set(max),
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Noop,
ConfigOp::Noop,
));
let mut some_existing_validator = AccountId::default();
for i in 0..max - validator_count {
let (_, controller) = testing_utils::create_stash_controller::<Test>(
i + 10_000_000,
100,
RewardDestination::Stash,
)
.unwrap();
assert_ok!(Staking::validate(
RuntimeOrigin::signed(controller),
ValidatorPrefs::default()
));
some_existing_validator = controller;
}
let (_, last_validator) =
testing_utils::create_stash_controller::<Test>(1337, 100, RewardDestination::Stash)
.unwrap();
assert_noop!(
Staking::validate(RuntimeOrigin::signed(last_validator), ValidatorPrefs::default()),
Error::<Test>::TooManyValidators,
);
let mut some_existing_nominator = AccountId::default();
for i in 0..max - nominator_count {
let (_, controller) = testing_utils::create_stash_controller::<Test>(
i + 20_000_000,
100,
RewardDestination::Stash,
)
.unwrap();
assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![11]));
some_existing_nominator = controller;
}
let (_, last_nominator) = testing_utils::create_stash_controller::<Test>(
30_000_000,
100,
RewardDestination::Stash,
)
.unwrap();
assert_noop!(
Staking::nominate(RuntimeOrigin::signed(last_nominator), vec![1]),
Error::<Test>::TooManyNominators
);
assert_ok!(Staking::nominate(RuntimeOrigin::signed(some_existing_nominator), vec![11]));
assert_ok!(Staking::validate(
RuntimeOrigin::signed(some_existing_validator),
ValidatorPrefs::default()
));
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Noop,
));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(last_nominator), vec![11]));
assert_ok!(Staking::validate(
RuntimeOrigin::signed(last_validator),
ValidatorPrefs::default()
));
})
}
}