use super::*;
use crate::session_rotation::Eras;
use frame_support::dispatch::{extract_actual_weight, GetDispatchInfo, WithPostDispatchInfo};
use sp_runtime::{bounded_btree_map, traits::Dispatchable};
#[test]
fn rewards_with_nominator_should_work() {
ExtBuilder::default().nominate(true).session_per_era(3).build_and_execute(|| {
let init_balance_11 = asset::total_balance::<T>(&11);
let init_balance_21 = asset::total_balance::<T>(&21);
let init_balance_101 = asset::total_balance::<T>(&101);
Payee::<T>::insert(11, RewardDestination::Account(11));
Payee::<T>::insert(21, RewardDestination::Account(21));
Payee::<T>::insert(101, RewardDestination::Account(101));
Eras::<T>::reward_active_era(vec![(11, 50)]);
Eras::<T>::reward_active_era(vec![(11, 50)]);
Eras::<T>::reward_active_era(vec![(21, 50)]);
let validator_payout_0 = validator_payout_for(time_per_era());
let maximum_payout = total_payout_for(time_per_era());
assert_eq_uvec!(Session::validators(), vec![11, 21]);
assert_eq!(asset::total_balance::<T>(&11), init_balance_11);
assert_eq!(asset::total_balance::<T>(&21), init_balance_21);
assert_eq!(asset::total_balance::<T>(&101), init_balance_101);
assert_eq!(
ErasRewardPoints::<T>::get(active_era()),
EraRewardPoints { total: 50 * 3, individual: bounded_btree_map![11 => 100, 21 => 50] }
);
let part_for_11 = Perbill::from_rational::<u32>(1000, 1250);
let part_for_21 = Perbill::from_rational::<u32>(1000, 1250);
let part_for_101_from_11 = Perbill::from_rational::<u32>(250, 1250);
let part_for_101_from_21 = Perbill::from_rational::<u32>(250, 1250);
Session::roll_until_active_era(2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 4, active_era: 1, planned_era: 2 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 5, active_era: 1, planned_era: 2 },
Event::EraPaid {
era_index: 1,
validator_payout: validator_payout_0,
remainder: maximum_payout - validator_payout_0
},
Event::SessionRotated { starting_session: 6, active_era: 2, planned_era: 2 }
]
);
assert_eq!(mock::RewardRemainderUnbalanced::get(), maximum_payout - validator_payout_0);
let pre_issuance = asset::total_issuance::<T>();
mock::make_all_reward_payment(1);
assert_eq!(
mock::staking_events_since_last_call(),
vec![
Event::PayoutStarted { era_index: 1, validator_stash: 11, page: 0, next: None },
Event::Rewarded { stash: 11, dest: RewardDestination::Account(11), amount: 4000 },
Event::Rewarded { stash: 101, dest: RewardDestination::Account(101), amount: 1000 },
Event::PayoutStarted { era_index: 1, validator_stash: 21, page: 0, next: None },
Event::Rewarded { stash: 21, dest: RewardDestination::Account(21), amount: 2000 },
Event::Rewarded { stash: 101, dest: RewardDestination::Account(101), amount: 500 }
]
);
let post_issuance = asset::total_issuance::<T>();
assert_eq!(post_issuance, pre_issuance + validator_payout_0);
assert_eq_error_rate!(
asset::total_balance::<T>(&11),
init_balance_11 + part_for_11 * validator_payout_0 * 2 / 3,
2,
);
assert_eq_error_rate!(
asset::total_balance::<T>(&21),
init_balance_21 + part_for_21 * validator_payout_0 * 1 / 3,
2,
);
assert_eq_error_rate!(
asset::total_balance::<T>(&101),
init_balance_101 +
part_for_101_from_11 * validator_payout_0 * 2 / 3 +
part_for_101_from_21 * validator_payout_0 * 1 / 3,
2
);
assert_eq_uvec!(Session::validators(), vec![11, 21]);
Eras::<T>::reward_active_era(vec![(11, 1)]);
let total_payout_1 = validator_payout_for(time_per_era());
Session::roll_until_active_era(3);
assert_eq!(
mock::RewardRemainderUnbalanced::get(),
maximum_payout * 2 - validator_payout_0 - total_payout_1,
);
assert_eq!(
mock::staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 7, active_era: 2, planned_era: 3 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 8, active_era: 2, planned_era: 3 },
Event::EraPaid { era_index: 2, validator_payout: 7500, remainder: 7500 },
Event::SessionRotated { starting_session: 9, active_era: 3, planned_era: 3 }
]
);
mock::make_all_reward_payment(2);
assert_eq!(asset::total_issuance::<T>(), post_issuance + total_payout_1);
assert_eq_error_rate!(
asset::total_balance::<T>(&11),
init_balance_11 + part_for_11 * (validator_payout_0 * 2 / 3 + total_payout_1),
2,
);
assert_eq_error_rate!(
asset::total_balance::<T>(&21),
init_balance_21 + part_for_21 * validator_payout_0 * 1 / 3,
2,
);
assert_eq_error_rate!(
asset::total_balance::<T>(&101),
init_balance_101 +
part_for_101_from_11 * (validator_payout_0 * 2 / 3 + total_payout_1) +
part_for_101_from_21 * validator_payout_0 * 1 / 3,
2
);
});
}
#[test]
fn rewards_no_nominator_should_work() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert_eq_uvec!(Session::validators(), vec![11, 21]);
assert_eq_uvec!(
era_exposures(1),
vec![
(11, Exposure::<AccountId, Balance> { total: 1000, own: 1000, others: vec![] }),
(21, Exposure::<AccountId, Balance> { total: 1000, own: 1000, others: vec![] })
]
);
reward_all_elected();
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
make_all_reward_payment(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::Staked, amount: 3750 },
Event::PayoutStarted { era_index: 1, validator_stash: 21, page: 0, next: None },
Event::Rewarded { stash: 21, dest: RewardDestination::Staked, amount: 3750 }
]
);
});
}
#[test]
fn nominating_and_rewards_should_work() {
ExtBuilder::default()
.nominate(false)
.set_status(41, StakerStatus::Validator)
.build_and_execute(|| {
assert_eq_uvec!(Session::validators(), vec![41, 21]);
bond_nominator(1, 5000, vec![11, 41]);
bond_virtual_nominator(3, 333, 5000, vec![11]);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::Bonded { stash: 1, amount: 5000 },
Event::Bonded { stash: 3, amount: 5000 },
]
);
Eras::<T>::reward_active_era(vec![(41, 1)]);
Eras::<T>::reward_active_era(vec![(21, 1)]);
Session::roll_until_active_era(2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 4, active_era: 1, planned_era: 2 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 5, active_era: 1, planned_era: 2 },
Event::EraPaid { era_index: 1, validator_payout: 7500, remainder: 7500 },
Event::SessionRotated { starting_session: 6, active_era: 2, planned_era: 2 }
]
);
assert_eq_uvec!(Session::validators(), vec![11, 41]);
assert_eq!(ErasStakersPaged::<T>::iter_prefix_values((active_era(),)).count(), 2);
assert_eq!(
Staking::eras_stakers(active_era(), &11),
Exposure {
total: 7500,
own: 1000,
others: vec![
IndividualExposure { who: 1, value: 1500 },
IndividualExposure { who: 3, value: 5000 }
]
}
);
assert_eq!(
Staking::eras_stakers(active_era(), &41),
Exposure {
total: 7500,
own: 4000,
others: vec![IndividualExposure { who: 1, value: 3500 }]
}
);
mock::make_all_reward_payment(1);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::PayoutStarted { era_index: 1, validator_stash: 21, page: 0, next: None },
Event::Rewarded { stash: 21, dest: RewardDestination::Staked, amount: 3750 },
Event::PayoutStarted { era_index: 1, validator_stash: 41, page: 0, next: None },
Event::Rewarded { stash: 41, dest: RewardDestination::Staked, amount: 3750 }
]
);
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: 11, page: 0, next: None },
Event::Rewarded { stash: 11, dest: RewardDestination::Staked, amount: 500 },
Event::Rewarded { stash: 1, dest: RewardDestination::Stash, amount: 750 },
Event::Rewarded {
stash: 3,
dest: RewardDestination::Account(333),
amount: 2500
},
Event::PayoutStarted { era_index: 2, validator_stash: 41, page: 0, next: None },
Event::Rewarded { stash: 41, dest: RewardDestination::Staked, amount: 2000 },
Event::Rewarded { stash: 1, dest: RewardDestination::Stash, amount: 1750 }
]
);
});
}
#[test]
fn reward_destination_staked() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert!(Session::validators().contains(&11));
assert_eq!(Staking::payee(11.into()), Some(RewardDestination::Staked));
assert_eq!(asset::total_balance::<T>(&11), 1001);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Eras::<T>::reward_active_era(vec![(11, 1)]);
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
mock::make_all_reward_payment(1);
assert_eq!(ClaimedRewards::<T>::get(1, &11), vec![0]);
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::Staked, amount: 7500 }
]
);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 8500,
active: 8500,
unlocking: Default::default(),
}
);
assert_eq!(asset::total_balance::<T>(&11), 1001 + 7500);
});
}
#[test]
fn reward_to_stake_works() {
ExtBuilder::default()
.nominate(false)
.set_status(31, StakerStatus::Idle)
.set_status(41, StakerStatus::Idle)
.set_stake(21, 2000)
.try_state(false)
.build_and_execute(|| {
assert_eq!(ValidatorCount::<T>::get(), 2);
assert!(<Validators<T>>::contains_key(&11) && <Validators<T>>::contains_key(&21));
assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000);
assert_eq!(Staking::eras_stakers(active_era(), &21).total, 2000);
let _ = asset::set_stakeable_balance::<T>(&10, 1000);
let _ = asset::set_stakeable_balance::<T>(&20, 1000);
Eras::<T>::upsert_exposure(0, &21, Exposure { total: 69, own: 69, others: vec![] });
<Ledger<T>>::insert(
&20,
StakingLedgerInspect {
stash: 21,
total: 69,
active: 69,
unlocking: Default::default(),
},
);
let validator_payout_0 = validator_payout_for(time_per_era());
Pallet::<T>::reward_by_ids(vec![(11, 1)]);
Pallet::<T>::reward_by_ids(vec![(21, 1)]);
Session::roll_until_active_era(2);
make_all_reward_payment(1);
assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000);
assert_eq!(Staking::eras_stakers(active_era(), &21).total, 2000);
let _11_balance = asset::stakeable_balance::<T>(&11);
assert_eq!(_11_balance, 1000 + validator_payout_0 / 2);
Session::roll_until_active_era(3);
assert_eq!(
Staking::eras_stakers(active_era(), &11).total,
1000 + validator_payout_0 / 2
);
assert_eq!(
Staking::eras_stakers(active_era(), &21).total,
2000 + validator_payout_0 / 2
);
});
}
#[test]
fn reward_destination_stash() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert!(Session::validators().contains(&11));
assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Stash));
assert_eq!(asset::total_balance::<T>(&11), 1001);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Eras::<T>::reward_active_era(vec![(11, 1)]);
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
mock::make_all_reward_payment(1);
assert_eq!(ClaimedRewards::<T>::get(1, &11), vec![0]);
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: 7500 }
]
);
assert_eq!(asset::total_balance::<T>(&11), 1001 + 7500);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
});
}
#[test]
fn reward_destination_account() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert!(Session::validators().contains(&11));
assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Account(7)));
assert_eq!(asset::total_balance::<T>(&11), 1001);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
Eras::<T>::reward_active_era(vec![(11, 1)]);
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
mock::make_all_reward_payment(1);
assert_eq!(ClaimedRewards::<T>::get(1, &11), vec![0]);
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::Account(7), amount: 7500 }
]
);
assert_eq!(asset::total_balance::<T>(&11), 1001);
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
assert_eq!(asset::total_balance::<T>(&7), 7500);
});
}
#[test]
fn validator_prefs_no_commission() {
ExtBuilder::default().build_and_execute(|| {
Eras::<T>::reward_active_era(vec![(11, 1)]);
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
mock::make_all_reward_payment(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::Staked, amount: 6000 },
Event::Rewarded { stash: 101, dest: RewardDestination::Staked, amount: 1500 }
]
);
});
}
#[test]
fn validator_prefs_100_commission() {
ExtBuilder::default().build_and_execute(|| {
let commission = Perbill::from_percent(100);
Eras::<T>::reward_active_era(vec![(11, 1)]);
Eras::<T>::set_validator_prefs(1, &11, ValidatorPrefs { commission, ..Default::default() });
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
mock::make_all_reward_payment(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::Staked, amount: 7500 }
]
);
});
}
#[test]
fn validator_payment_some_commission_prefs_work() {
ExtBuilder::default().build_and_execute(|| {
let commission = Perbill::from_percent(40);
Eras::<T>::reward_active_era(vec![(11, 1)]);
Eras::<T>::set_validator_prefs(1, &11, ValidatorPrefs { commission, ..Default::default() });
Session::roll_until_active_era(2);
let _ = staking_events_since_last_call();
mock::make_all_reward_payment(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::Staked, amount: 6600 },
Event::Rewarded { stash: 101, dest: RewardDestination::Staked, amount: 900 }
]
);
});
}
#[test]
fn min_commission_works() {
ExtBuilder::default().build_and_execute(|| {
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false }
));
assert_eq!(
*staking_events().last().unwrap(),
Event::ValidatorPrefsSet {
stash: 11,
prefs: ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false }
}
);
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Set(Perbill::from_percent(10)),
ConfigOp::Noop,
ConfigOp::Noop,
));
assert_noop!(
Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false }
),
Error::<T>::CommissionTooLow
);
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(10), blocked: false }
));
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(15), blocked: false }
));
})
}
#[test]
fn set_min_commission_works_with_admin_origin() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(MinCommission::<T>::get(), Zero::zero());
assert_ok!(Staking::set_min_commission(RuntimeOrigin::root(), Perbill::from_percent(10)));
assert_eq!(MinCommission::<T>::get(), Perbill::from_percent(10));
assert_noop!(
Staking::set_min_commission(RuntimeOrigin::signed(2), Perbill::from_percent(15)),
BadOrigin
);
assert_ok!(Staking::set_min_commission(
RuntimeOrigin::signed(1),
Perbill::from_percent(15),
));
assert_noop!(
Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(14), blocked: false }
),
Error::<T>::CommissionTooLow
);
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(15), blocked: false }
));
})
}
#[test]
fn force_apply_min_commission_works() {
let prefs = |c| ValidatorPrefs { commission: Perbill::from_percent(c), blocked: false };
let validators = || Validators::<T>::iter().collect::<Vec<_>>();
ExtBuilder::default().build_and_execute(|| {
assert_ok!(Staking::validate(RuntimeOrigin::signed(31), prefs(10)));
assert_ok!(Staking::validate(RuntimeOrigin::signed(21), prefs(5)));
assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]);
MinCommission::<T>::set(Perbill::from_percent(5));
assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 31));
assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]);
assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 21));
assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]);
assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 11));
assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(5))]);
assert_noop!(
Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 420),
Error::<T>::NotStash
);
});
}
#[test]
fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() {
ExtBuilder::default().nominate(true).build_and_execute(|| {
let err_weight = <T as Config>::WeightInfo::payout_stakers_alive_staked(0);
Payee::<T>::insert(11, RewardDestination::Account(11));
Payee::<T>::insert(101, RewardDestination::Account(101));
Pallet::<T>::reward_by_ids(vec![(11, 1)]);
Session::roll_until_active_era(2);
Pallet::<T>::reward_by_ids(vec![(11, 1)]);
Session::roll_until_active_era(3);
Pallet::<T>::reward_by_ids(vec![(11, 1)]);
Session::roll_until_active_era(HistoryDepth::get() + 1);
let _ = staking_events_since_last_call();
assert_noop!(
Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 0),
Error::<T>::InvalidEraToReward.with_weight(err_weight)
);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0));
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::Account(11), amount: 6000 },
Event::Rewarded { stash: 101, dest: RewardDestination::Account(101), amount: 1500 }
]
);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0));
assert_eq!(
staking_events_since_last_call(),
vec![
Event::PayoutStarted { era_index: 2, validator_stash: 11, page: 0, next: None },
Event::Rewarded { stash: 11, dest: RewardDestination::Account(11), amount: 6000 },
Event::Rewarded { stash: 101, dest: RewardDestination::Account(101), amount: 1500 }
]
);
assert_noop!(
Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, active_era(), 0),
Error::<T>::InvalidEraToReward.with_weight(err_weight)
);
});
}
#[test]
fn nominators_over_max_exposure_page_size_are_rewarded() {
ExtBuilder::default().build_and_execute(|| {
for i in 0..=MaxExposurePageSize::get() {
let stash = 10_000 + i as AccountId;
let balance = 10_000 + i as Balance;
asset::set_stakeable_balance::<T>(&stash, balance);
assert_ok!(Staking::bond(
RuntimeOrigin::signed(stash),
balance,
RewardDestination::Stash
));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(stash), vec![11]));
}
Session::roll_until_active_era(2);
Pallet::<T>::reward_by_ids(vec![(11, 1)]);
Session::roll_until_active_era(3);
mock::make_all_reward_payment(2);
let mut i: u32 = 0;
while i < MaxExposurePageSize::get() {
let stash = 10_000 + i as AccountId;
let balance = 10_000 + i as Balance;
assert!(asset::stakeable_balance::<T>(&stash) > balance);
i += 1;
}
let stash = 10_000 + i as AccountId;
assert!(asset::stakeable_balance::<T>(&stash) > (10_000 + i) as Balance);
});
}
#[test]
fn test_nominators_are_rewarded_for_all_exposure_page() {
ExtBuilder::default().build_and_execute(|| {
let nominator_count = 2 * MaxExposurePageSize::get() + 1;
for i in 0..nominator_count {
let stash = 10_000 + i as AccountId;
let balance = 10_000 + i as Balance;
asset::set_stakeable_balance::<T>(&stash, balance);
assert_ok!(Staking::bond(
RuntimeOrigin::signed(stash),
balance,
RewardDestination::Stash
));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(stash), vec![11]));
}
Session::roll_until_active_era(2);
Pallet::<T>::reward_by_ids(vec![(11, 1)]);
Session::roll_until_active_era(3);
mock::make_all_reward_payment(2);
assert_eq!(Eras::<T>::exposure_page_count(2, &11), 3);
for i in 0..nominator_count {
let current_balance = asset::stakeable_balance::<T>(&((10000 + i) as AccountId));
let previous_balance = asset::stakeable_balance::<T>(&((10000 + i - 1) as AccountId));
let original_balance = 10_000 + i as Balance;
assert!(current_balance > original_balance);
assert!(current_balance > previous_balance);
}
});
}
#[test]
fn test_multi_page_payout_stakers_by_page() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
let balance = 1000;
let mut total_exposure = balance;
bond_validator(11, balance); assert_eq!(Validators::<T>::count(), 1);
for i in 0..100 {
let bond_amount = balance + i as Balance;
bond_nominator(1000 + i, bond_amount, vec![11]);
total_exposure += bond_amount;
}
Session::roll_until_active_era(2);
Staking::reward_by_ids(vec![(11, 1)]);
assert_eq!(MaxExposurePageSize::get(), 64);
assert_eq!(Eras::<T>::exposure_page_count(2, &11), 2);
let payout = validator_payout_for(time_per_era());
Session::roll_until_active_era(3);
let actual_exposure_0 = Eras::<T>::get_paged_exposure(2, &11, 0).unwrap();
assert_eq!(actual_exposure_0.total(), total_exposure);
assert_eq!(actual_exposure_0.own(), 1000);
assert_eq!(actual_exposure_0.others().len(), 64);
let actual_exposure_1 = Eras::<T>::get_paged_exposure(2, &11, 1).unwrap();
assert_eq!(actual_exposure_1.total(), total_exposure);
assert_eq!(actual_exposure_1.own(), 0);
assert_eq!(actual_exposure_1.others().len(), 100 - 64);
let pre_payout_total_issuance = pallet_balances::TotalIssuance::<T>::get();
RewardOnUnbalanceWasCalled::set(false);
let _ = staking_events_since_last_call();
let controller_balance_before_p0_payout = asset::stakeable_balance::<T>(&11);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0));
assert!(matches!(
staking_events_since_last_call().as_slice(),
&[
Event::PayoutStarted { era_index: 2, validator_stash: 11, page: 0, next: Some(1) },
..,
Event::Rewarded { stash: 1063, dest: RewardDestination::Stash, amount: _ },
Event::Rewarded { stash: 1064, dest: RewardDestination::Stash, amount: _ },
]
));
let controller_balance_after_p0_payout = asset::stakeable_balance::<T>(&11);
assert!(pallet_balances::TotalIssuance::<T>::get() > pre_payout_total_issuance);
assert!(pallet_balances::TotalIssuance::<T>::get() < pre_payout_total_issuance + payout);
assert!(controller_balance_after_p0_payout > controller_balance_before_p0_payout);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 1));
let events = staking_events_since_last_call();
assert!(matches!(
events.as_slice(),
&[
Event::PayoutStarted { era_index: 2, validator_stash: 11, page: 1, next: None },
Event::Rewarded { stash: 1065, dest: RewardDestination::Stash, amount: _ },
Event::Rewarded { stash: 1066, dest: RewardDestination::Stash, amount: _ },
..
]
));
assert_eq!(asset::stakeable_balance::<T>(&11), controller_balance_after_p0_payout);
assert_eq_error_rate!(
pallet_balances::TotalIssuance::<T>::get(),
pre_payout_total_issuance + payout,
2
);
assert!(RewardOnUnbalanceWasCalled::get());
assert!(asset::stakeable_balance::<T>(&11) > balance);
for i in 0..100 {
assert!(asset::stakeable_balance::<T>(&(1000 + i)) > balance + i as Balance);
}
for page in 0..Eras::<T>::exposure_page_count(2, &11) {
assert_eq!(Eras::<T>::is_rewards_claimed(2, &11, page), true);
}
for i in 4..17 {
Staking::reward_by_ids(vec![(11, 1)]);
let payout = validator_payout_for(time_per_era());
let pre_payout_total_issuance = pallet_balances::TotalIssuance::<T>::get();
Session::roll_until_active_era(i);
RewardOnUnbalanceWasCalled::set(false);
mock::make_all_reward_payment(i - 1);
assert_eq_error_rate!(
pallet_balances::TotalIssuance::<T>::get(),
pre_payout_total_issuance + payout,
2
);
assert!(RewardOnUnbalanceWasCalled::get());
for page in 0..Eras::<T>::exposure_page_count(i - 1, &11) {
assert_eq!(Eras::<T>::is_rewards_claimed(i - 1, &11, page), true);
}
}
assert_eq!(ClaimedRewards::<T>::get(14, &11), vec![0, 1]);
let last_era = 99;
let history_depth = HistoryDepth::get();
let last_reward_era = last_era - 1;
let first_claimable_reward_era = last_era - history_depth;
for i in 17..=last_era {
Staking::reward_by_ids(vec![(11, 1)]);
let _ = validator_payout_for(time_per_era());
Session::roll_until_active_era(i);
}
for era in 2..15 {
assert!(!ClaimedRewards::<T>::get(era, &11).is_empty());
}
assert_ok!(Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
first_claimable_reward_era,
0
));
assert_eq!(ClaimedRewards::<T>::get(first_claimable_reward_era, &11), vec![0]);
assert_ok!(Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
first_claimable_reward_era,
1
));
assert_eq!(ClaimedRewards::<T>::get(first_claimable_reward_era, &11), vec![0, 1]);
assert_ok!(Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
last_reward_era,
0
));
assert_eq!(ClaimedRewards::<T>::get(last_reward_era, &11), vec![0]);
assert_ok!(Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
last_reward_era,
1
));
assert_eq!(ClaimedRewards::<T>::get(last_reward_era, &11), vec![0, 1]);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 69, 0));
assert_eq!(ClaimedRewards::<T>::get(69, &11), vec![0]);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 23, 1));
assert_eq!(ClaimedRewards::<T>::get(23, &11), vec![1]);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 42, 0));
assert_eq!(ClaimedRewards::<T>::get(42, &11), vec![0]);
});
}
#[test]
fn test_multi_page_payout_stakers_backward_compatible() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
let balance = 1000;
let mut total_exposure = balance;
bond_validator(11, balance); assert_eq!(Validators::<T>::count(), 1);
let err_weight = <T as Config>::WeightInfo::payout_stakers_alive_staked(0);
for i in 0..100 {
let bond_amount = balance + i as Balance;
bond_nominator(1000 + i, bond_amount, vec![11]);
total_exposure += bond_amount;
}
Session::roll_until_active_era(2);
Staking::reward_by_ids(vec![(11, 1)]);
assert_eq!(Eras::<T>::exposure_page_count(2, &11), 2);
let payout = validator_payout_for(time_per_era());
Session::roll_until_active_era(3);
let actual_exposure_0 = Eras::<T>::get_paged_exposure(2, &11, 0).unwrap();
assert_eq!(actual_exposure_0.total(), total_exposure);
assert_eq!(actual_exposure_0.own(), 1000);
assert_eq!(actual_exposure_0.others().len(), 64);
let actual_exposure_1 = Eras::<T>::get_paged_exposure(2, &11, 1).unwrap();
assert_eq!(actual_exposure_1.total(), total_exposure);
assert_eq!(actual_exposure_1.own(), 0);
assert_eq!(actual_exposure_1.others().len(), 100 - 64);
let pre_payout_total_issuance = pallet_balances::TotalIssuance::<T>::get();
RewardOnUnbalanceWasCalled::set(false);
let controller_balance_before_p0_payout = asset::stakeable_balance::<T>(&11);
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2));
assert_noop!(
Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
let controller_balance_after_p0_payout = asset::stakeable_balance::<T>(&11);
assert!(pallet_balances::TotalIssuance::<T>::get() > pre_payout_total_issuance);
assert!(pallet_balances::TotalIssuance::<T>::get() < pre_payout_total_issuance + payout);
assert!(controller_balance_after_p0_payout > controller_balance_before_p0_payout);
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2));
assert_noop!(
Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 2),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
assert_eq!(asset::stakeable_balance::<T>(&11), controller_balance_after_p0_payout);
assert_eq_error_rate!(
pallet_balances::TotalIssuance::<T>::get(),
pre_payout_total_issuance + payout,
2
);
assert!(RewardOnUnbalanceWasCalled::get());
assert!(asset::stakeable_balance::<T>(&11) > balance);
for i in 0..100 {
assert!(asset::stakeable_balance::<T>(&(1000 + i)) > balance + i as Balance);
}
for page in 0..Eras::<T>::exposure_page_count(2, &11) {
assert_eq!(Eras::<T>::is_rewards_claimed(2, &11, page), true);
}
for i in 4..17 {
Staking::reward_by_ids(vec![(11, 1)]);
let payout = validator_payout_for(time_per_era());
let pre_payout_total_issuance = pallet_balances::TotalIssuance::<T>::get();
Session::roll_until_active_era(i);
RewardOnUnbalanceWasCalled::set(false);
mock::make_all_reward_payment(i - 1);
assert_eq_error_rate!(
pallet_balances::TotalIssuance::<T>::get(),
pre_payout_total_issuance + payout,
2
);
assert!(RewardOnUnbalanceWasCalled::get());
for page in 0..Eras::<T>::exposure_page_count(i - 1, &11) {
assert_eq!(Eras::<T>::is_rewards_claimed(i - 1, &11, page), true);
}
}
assert_eq!(ClaimedRewards::<T>::get(14, &11), vec![0, 1]);
let last_era = 99;
let history_depth = HistoryDepth::get();
let last_reward_era = last_era - 1;
let first_claimable_reward_era = last_era - history_depth;
for i in 17..=last_era {
Staking::reward_by_ids(vec![(11, 1)]);
let _ = validator_payout_for(time_per_era());
Session::roll_until_active_era(i);
}
for era in 2..15 {
assert!(!ClaimedRewards::<T>::get(era, &11).is_empty());
}
assert_ok!(Staking::payout_stakers(
RuntimeOrigin::signed(1337),
11,
first_claimable_reward_era
));
assert_eq!(ClaimedRewards::<T>::get(first_claimable_reward_era, &11), vec![0]);
assert_ok!(Staking::payout_stakers(
RuntimeOrigin::signed(1337),
11,
first_claimable_reward_era,
));
assert_eq!(ClaimedRewards::<T>::get(first_claimable_reward_era, &11), vec![0, 1]);
assert_ok!(Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
last_reward_era,
1
));
assert_eq!(ClaimedRewards::<T>::get(last_reward_era, &11), vec![1]);
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, last_reward_era,));
assert_eq!(ClaimedRewards::<T>::get(last_reward_era, &11), vec![1, 0]);
assert_noop!(
Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, last_reward_era),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
for i in 100..200 {
let bond_amount = balance + i as Balance;
bond_nominator(1000 + i, bond_amount, vec![11]);
}
let test_era = last_era + 1;
Session::roll_until_active_era(test_era);
Staking::reward_by_ids(vec![(11, 1)]);
let _ = validator_payout_for(time_per_era());
Session::roll_until_active_era(test_era + 1);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, test_era, 2));
assert_eq!(ClaimedRewards::<T>::get(test_era, &11), vec![2]);
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era));
assert_eq!(ClaimedRewards::<T>::get(test_era, &11), vec![2, 0]);
assert_noop!(
Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, test_era, 2),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era));
assert_eq!(ClaimedRewards::<T>::get(test_era, &11), vec![2, 0, 1]);
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, test_era));
assert_eq!(ClaimedRewards::<T>::get(test_era, &11), vec![2, 0, 1, 3]);
});
}
#[test]
fn test_page_count_and_size() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
let balance = 1000;
bond_validator(11, balance); assert_eq!(Validators::<T>::count(), 1);
for i in 0..100 {
let bond_amount = balance + i as Balance;
bond_nominator(1000 + i, bond_amount, vec![11]);
}
Session::roll_until_active_era(2);
assert_eq!(MaxExposurePageSize::get(), 64);
assert_eq!(Eras::<T>::exposure_page_count(2, &11), 2);
assert_eq!(Eras::<T>::get_paged_exposure(2, &11, 0).unwrap().others().len(), 64);
assert_eq!(Eras::<T>::get_paged_exposure(2, &11, 1).unwrap().others().len(), 36);
MaxExposurePageSize::set(32);
Session::roll_until_active_era(3);
assert_eq!(Eras::<T>::exposure_page_count(3, &11), 4);
assert_eq!(Eras::<T>::get_paged_exposure(3, &11, 0).unwrap().others().len(), 32);
assert_eq!(Eras::<T>::get_paged_exposure(3, &11, 1).unwrap().others().len(), 32);
assert_eq!(Eras::<T>::get_paged_exposure(3, &11, 2).unwrap().others().len(), 32);
assert_eq!(Eras::<T>::get_paged_exposure(3, &11, 3).unwrap().others().len(), 4);
MaxExposurePageSize::set(5);
Session::roll_until_active_era(4);
assert_eq!(Eras::<T>::exposure_page_count(4, &11), 20);
});
}
#[test]
fn payout_stakers_handles_basic_errors() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
let err_weight = <T as Config>::WeightInfo::payout_stakers_alive_staked(0);
let balance = 1000;
bond_validator(11, balance);
for i in 0..100 {
bond_nominator(1000 + i, balance + i as Balance, vec![11]);
}
Session::roll_until_active_era(2);
Staking::reward_by_ids(vec![(11, 1)]);
let _ = validator_payout_for(time_per_era());
Session::roll_until_active_era(3);
assert_noop!(
Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 3, 0),
Error::<T>::InvalidEraToReward.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 10, 2, 0),
Error::<T>::NotStash.with_weight(err_weight)
);
let last_era = 99;
for i in 4..=last_era {
Staking::reward_by_ids(vec![(11, 1)]);
let _ = validator_payout_for(time_per_era());
Session::roll_until_active_era(i);
}
let history_depth = HistoryDepth::get();
let expected_last_reward_era = last_era - 1;
let expected_start_reward_era = last_era - history_depth;
assert_noop!(
Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_start_reward_era - 1,
0
),
Error::<T>::InvalidEraToReward.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_last_reward_era + 1,
0
),
Error::<T>::InvalidEraToReward.with_weight(err_weight)
);
assert_ok!(Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_start_reward_era,
0
));
assert_ok!(Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_last_reward_era,
0
));
assert_ok!(Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_last_reward_era,
1
));
assert_noop!(
Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_start_reward_era,
0
),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_last_reward_era,
0
),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_last_reward_era,
1
),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers_by_page(
RuntimeOrigin::signed(1337),
11,
expected_last_reward_era,
2
),
Error::<T>::InvalidPage.with_weight(err_weight)
);
});
}
#[test]
fn test_commission_paid_across_pages() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
let balance = 1;
let commission = 50;
bond_validator(11, balance);
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(commission), blocked: false }
));
assert_eq!(Validators::<T>::count(), 1);
for i in 0..200 {
let bond_amount = balance + i as Balance;
bond_nominator(1000 + i, bond_amount, vec![11]);
}
Session::roll_until_active_era(2);
Staking::reward_by_ids(vec![(11, 1)]);
assert_eq!(Eras::<T>::exposure_page_count(2, &11), 4);
let payout = validator_payout_for(time_per_era());
Session::roll_until_active_era(3);
let initial_balance = asset::stakeable_balance::<T>(&11);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, 0));
let controller_balance_after_p0_payout = asset::stakeable_balance::<T>(&11);
assert!(initial_balance < controller_balance_after_p0_payout);
for i in 1..4 {
let before_balance = asset::stakeable_balance::<T>(&11);
assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 2, i));
let after_balance = asset::stakeable_balance::<T>(&11);
assert!(before_balance < after_balance);
}
assert_eq_error_rate!(asset::stakeable_balance::<T>(&11), initial_balance + payout / 2, 1,);
});
}
#[test]
fn payout_stakers_handles_weight_refund() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
use crate::Call as StakingCall;
let max_nom_rewarded = MaxExposurePageSize::get();
assert!(max_nom_rewarded >= 4);
let half_max_nom_rewarded = max_nom_rewarded / 2;
assert!(half_max_nom_rewarded > 0);
assert!(max_nom_rewarded > half_max_nom_rewarded);
let max_nom_rewarded_weight =
<T as Config>::WeightInfo::payout_stakers_alive_staked(max_nom_rewarded);
let half_max_nom_rewarded_weight =
<T as Config>::WeightInfo::payout_stakers_alive_staked(half_max_nom_rewarded);
let zero_nom_payouts_weight = <T as Config>::WeightInfo::payout_stakers_alive_staked(0);
assert!(zero_nom_payouts_weight.any_gt(Weight::zero()));
assert!(half_max_nom_rewarded_weight.any_gt(zero_nom_payouts_weight));
assert!(max_nom_rewarded_weight.any_gt(half_max_nom_rewarded_weight));
let balance = 1000;
bond_validator(11, balance);
Session::roll_until_active_era(2);
Staking::reward_by_ids(vec![(11, 1)]);
for i in 0..half_max_nom_rewarded {
bond_nominator((1000 + i).into(), balance + i as Balance, vec![11]);
}
Session::roll_until_active_era(3);
let call = RuntimeCall::Staking(StakingCall::payout_stakers_by_page {
validator_stash: 11,
era: 2,
page: 0,
});
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert_ok!(result);
assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);
Session::roll_until_active_era(4);
let call = RuntimeCall::Staking(StakingCall::payout_stakers_by_page {
validator_stash: 11,
era: 3,
page: 0,
});
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert_ok!(result);
assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);
Staking::reward_by_ids(vec![(11, 1)]);
Session::roll_until_active_era(5);
let call = RuntimeCall::Staking(StakingCall::payout_stakers_by_page {
validator_stash: 11,
era: 4,
page: 0,
});
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert_ok!(result);
assert_eq!(extract_actual_weight(&result, &info), half_max_nom_rewarded_weight);
for i in half_max_nom_rewarded..max_nom_rewarded {
bond_nominator((1000 + i).into(), balance + i as Balance, vec![11]);
}
Session::roll_until_active_era(6);
Staking::reward_by_ids(vec![(11, 1)]);
Session::roll_until_active_era(7);
let call = RuntimeCall::Staking(StakingCall::payout_stakers_by_page {
validator_stash: 11,
era: 6,
page: 0,
});
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert_ok!(result);
assert_eq!(extract_actual_weight(&result, &info), max_nom_rewarded_weight);
let call = RuntimeCall::Staking(StakingCall::payout_stakers_by_page {
validator_stash: 11,
era: 6,
page: 0,
});
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert!(result.is_err());
assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);
});
}
#[test]
fn test_runtime_api_pending_rewards() {
ExtBuilder::default().build_and_execute(|| {
let err_weight = <T as Config>::WeightInfo::payout_stakers_alive_staked(0);
let stake = 100;
let validator_one = 301;
let validator_two = 302;
let validator_three = 303;
for v in validator_one..=validator_three {
let _ = asset::set_stakeable_balance::<T>(&v, stake);
assert_ok!(Staking::bond(RuntimeOrigin::signed(v), stake, RewardDestination::Staked));
}
let reward = EraRewardPoints {
total: 1,
individual: bounded_btree_map![validator_one => 1, validator_two => 1, validator_three => 1],
};
ErasRewardPoints::<T>::insert(0, reward);
let mut individual_exposures: Vec<IndividualExposure<AccountId, Balance>> = vec![];
for i in 0..=MaxExposurePageSize::get() {
individual_exposures.push(IndividualExposure { who: i.into(), value: stake });
}
let exposure = Exposure::<AccountId, Balance> {
total: stake * (MaxExposurePageSize::get() as Balance + 2),
own: stake,
others: individual_exposures,
};
Eras::<T>::upsert_exposure(0, &validator_one, exposure.clone());
Eras::<T>::upsert_exposure(0, &validator_two, exposure.clone());
ErasValidatorReward::<T>::insert(0, 1000);
assert!(Eras::<T>::pending_rewards(0, &validator_one));
assert!(Eras::<T>::pending_rewards(0, &validator_two));
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), validator_one, 0));
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), validator_two, 0));
assert!(Eras::<T>::pending_rewards(0, &validator_one));
assert!(Eras::<T>::pending_rewards(0, &validator_two));
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), validator_one, 0));
assert!(!Eras::<T>::pending_rewards(0, &validator_one));
assert_noop!(
Staking::payout_stakers(RuntimeOrigin::signed(1337), validator_one, 0),
Error::<T>::AlreadyClaimed.with_weight(err_weight)
);
assert!(Eras::<T>::pending_rewards(0, &validator_two));
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), validator_two, 0));
});
}