1use super::*;
19use crate::{self as pools};
20use frame_support::{
21 assert_ok, derive_impl, ord_parameter_types, parameter_types,
22 traits::{fungible::Mutate, VariantCountOf},
23 PalletId,
24};
25use frame_system::{EnsureSignedBy, RawOrigin};
26use sp_runtime::{BuildStorage, DispatchResult, FixedU128};
27use sp_staking::{
28 Agent, DelegationInterface, DelegationMigrator, Delegator, OnStakingUpdate, Stake,
29};
30
31pub type BlockNumber = u64;
32pub type AccountId = u128;
33pub type Balance = u128;
34pub type RewardCounter = FixedU128;
35pub type T = Runtime;
38pub type Currency = <T as Config>::Currency;
39
40pub fn default_bonded_account() -> AccountId {
42 Pools::generate_bonded_account(1)
43}
44
45pub fn default_reward_account() -> AccountId {
47 Pools::generate_reward_account(1)
48}
49
50parameter_types! {
51 pub static MinJoinBondConfig: Balance = 2;
52 pub static CurrentEra: EraIndex = 0;
53 pub static BondingDuration: EraIndex = 3;
54 pub storage BondedBalanceMap: BTreeMap<AccountId, Balance> = Default::default();
55 pub storage UnbondingBalanceMap: BTreeMap<AccountId, Vec<(EraIndex, Balance)>> = Default::default();
57 #[derive(Clone, PartialEq)]
58 pub static MaxUnbonding: u32 = 8;
59 pub static StakingMinBond: Balance = 10;
60 pub storage Nominations: Option<Vec<AccountId>> = None;
61 pub static RestrictedAccounts: Vec<AccountId> = Vec::new();
62}
63pub struct StakingMock;
64
65impl StakingMock {
66 pub(crate) fn set_bonded_balance(who: AccountId, bonded: Balance) {
67 let mut x = BondedBalanceMap::get();
68 x.insert(who, bonded);
69 BondedBalanceMap::set(&x)
70 }
71 pub fn slash_by(pool_id: PoolId, amount: Balance) {
78 let acc = Pools::generate_bonded_account(pool_id);
79 let bonded = BondedBalanceMap::get();
80 let pre_total = bonded.get(&acc).unwrap();
81 Self::set_bonded_balance(acc, pre_total - amount);
82 DelegateMock::on_slash(acc, amount);
83 Pools::on_slash(&acc, pre_total - amount, &Default::default(), amount);
84 }
85}
86
87impl sp_staking::StakingInterface for StakingMock {
88 type Balance = Balance;
89 type AccountId = AccountId;
90 type CurrencyToVote = ();
91
92 fn minimum_nominator_bond() -> Self::Balance {
93 StakingMinBond::get()
94 }
95 fn minimum_validator_bond() -> Self::Balance {
96 StakingMinBond::get()
97 }
98
99 fn desired_validator_count() -> u32 {
100 unimplemented!("method currently not used in testing")
101 }
102
103 fn current_era() -> EraIndex {
104 CurrentEra::get()
105 }
106
107 fn bonding_duration() -> EraIndex {
108 BondingDuration::get()
109 }
110
111 fn status(
112 _: &Self::AccountId,
113 ) -> Result<sp_staking::StakerStatus<Self::AccountId>, DispatchError> {
114 Nominations::get()
115 .map(|noms| sp_staking::StakerStatus::Nominator(noms))
116 .ok_or(DispatchError::Other("NotStash"))
117 }
118
119 fn is_virtual_staker(who: &Self::AccountId) -> bool {
120 AgentBalanceMap::get().contains_key(who)
121 }
122
123 fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
124 let mut x = BondedBalanceMap::get();
125 x.get_mut(who).map(|v| *v += extra);
126 BondedBalanceMap::set(&x);
127 Ok(())
128 }
129
130 fn unbond(who: &Self::AccountId, amount: Self::Balance) -> DispatchResult {
131 let mut x = BondedBalanceMap::get();
132 *x.get_mut(who).unwrap() = x.get_mut(who).unwrap().saturating_sub(amount);
133 BondedBalanceMap::set(&x);
134
135 let era = Self::current_era();
136 let unlocking_at = era + Self::bonding_duration();
137 let mut y = UnbondingBalanceMap::get();
138 y.entry(*who).or_insert(Default::default()).push((unlocking_at, amount));
139 UnbondingBalanceMap::set(&y);
140 Ok(())
141 }
142
143 fn set_payee(_stash: &Self::AccountId, _reward_acc: &Self::AccountId) -> DispatchResult {
144 unimplemented!("method currently not used in testing")
145 }
146
147 fn chill(_: &Self::AccountId) -> sp_runtime::DispatchResult {
148 Ok(())
149 }
150
151 fn withdraw_unbonded(who: Self::AccountId, _: u32) -> Result<bool, DispatchError> {
152 let mut unbonding_map = UnbondingBalanceMap::get();
153
154 let unlocking = |pair: &Vec<(EraIndex, Balance)>| -> Balance {
156 pair.iter()
157 .try_fold(Zero::zero(), |acc: Balance, (_at, amount)| acc.checked_add(*amount))
158 .unwrap()
159 };
160
161 let staker_map = unbonding_map.get_mut(&who).ok_or("Nothing to unbond")?;
162 let unlocking_before = unlocking(&staker_map);
163
164 let current_era = Self::current_era();
165
166 staker_map.retain(|(unlocking_at, _amount)| *unlocking_at > current_era);
167
168 let withdraw_amount = unlocking_before.saturating_sub(unlocking(&staker_map));
170 Pools::on_withdraw(&who, withdraw_amount);
171 DelegateMock::on_withdraw(who, withdraw_amount);
172
173 UnbondingBalanceMap::set(&unbonding_map);
174 Ok(UnbondingBalanceMap::get().get(&who).unwrap().is_empty() &&
175 BondedBalanceMap::get().get(&who).unwrap().is_zero())
176 }
177
178 fn bond(stash: &Self::AccountId, value: Self::Balance, _: &Self::AccountId) -> DispatchResult {
179 StakingMock::set_bonded_balance(*stash, value);
180 Ok(())
181 }
182
183 fn nominate(_: &Self::AccountId, nominations: Vec<Self::AccountId>) -> DispatchResult {
184 Nominations::set(&Some(nominations));
185 Ok(())
186 }
187
188 #[cfg(feature = "runtime-benchmarks")]
189 fn nominations(_: &Self::AccountId) -> Option<Vec<Self::AccountId>> {
190 Nominations::get()
191 }
192
193 fn stash_by_ctrl(_controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError> {
194 unimplemented!("method currently not used in testing")
195 }
196
197 fn stake(who: &Self::AccountId) -> Result<Stake<Balance>, DispatchError> {
198 match (UnbondingBalanceMap::get().get(who), BondedBalanceMap::get().get(who).copied()) {
199 (None, None) => Err(DispatchError::Other("balance not found")),
200 (Some(v), None) => Ok(Stake {
201 total: v.into_iter().fold(0u128, |acc, &x| acc.saturating_add(x.1)),
202 active: 0,
203 }),
204 (None, Some(v)) => Ok(Stake { total: v, active: v }),
205 (Some(a), Some(b)) => Ok(Stake {
206 total: a.into_iter().fold(0u128, |acc, &x| acc.saturating_add(x.1)) + b,
207 active: b,
208 }),
209 }
210 }
211
212 fn election_ongoing() -> bool {
213 unimplemented!("method currently not used in testing")
214 }
215
216 fn force_unstake(_who: Self::AccountId) -> sp_runtime::DispatchResult {
217 unimplemented!("method currently not used in testing")
218 }
219
220 fn is_exposed_in_era(_who: &Self::AccountId, _era: &EraIndex) -> bool {
221 unimplemented!("method currently not used in testing")
222 }
223
224 #[cfg(feature = "runtime-benchmarks")]
225 fn add_era_stakers(
226 _current_era: &EraIndex,
227 _stash: &Self::AccountId,
228 _exposures: Vec<(Self::AccountId, Self::Balance)>,
229 ) {
230 unimplemented!("method currently not used in testing")
231 }
232
233 #[cfg(feature = "runtime-benchmarks")]
234 fn set_current_era(_era: EraIndex) {
235 unimplemented!("method currently not used in testing")
236 }
237
238 #[cfg(feature = "runtime-benchmarks")]
239 fn max_exposure_page_size() -> sp_staking::Page {
240 unimplemented!("method currently not used in testing")
241 }
242
243 fn slash_reward_fraction() -> Perbill {
244 unimplemented!("method currently not used in testing")
245 }
246}
247
248parameter_types! {
249 pub storage AgentBalanceMap: BTreeMap<AccountId, (Balance, Balance, Balance)> = Default::default();
251 pub storage DelegatorBalanceMap: BTreeMap<AccountId, Balance> = Default::default();
252}
253pub struct DelegateMock;
254impl DelegationInterface for DelegateMock {
255 type Balance = Balance;
256 type AccountId = AccountId;
257 fn agent_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance> {
258 AgentBalanceMap::get()
259 .get(&agent.get())
260 .copied()
261 .map(|(delegated, _, pending)| delegated - pending)
262 }
263
264 fn agent_transferable_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance> {
265 AgentBalanceMap::get()
266 .get(&agent.get())
267 .copied()
268 .map(|(_, unclaimed_withdrawals, _)| unclaimed_withdrawals)
269 }
270
271 fn delegator_balance(delegator: Delegator<Self::AccountId>) -> Option<Self::Balance> {
272 DelegatorBalanceMap::get().get(&delegator.get()).copied()
273 }
274
275 fn register_agent(
276 agent: Agent<Self::AccountId>,
277 _reward_account: &Self::AccountId,
278 ) -> DispatchResult {
279 let mut agents = AgentBalanceMap::get();
280 agents.insert(agent.get(), (0, 0, 0));
281 AgentBalanceMap::set(&agents);
282 Ok(())
283 }
284
285 fn remove_agent(agent: Agent<Self::AccountId>) -> DispatchResult {
286 let mut agents = AgentBalanceMap::get();
287 let agent = agent.get();
288 assert!(agents.contains_key(&agent));
289 agents.remove(&agent);
290 AgentBalanceMap::set(&agents);
291 Ok(())
292 }
293
294 fn delegate(
295 delegator: Delegator<Self::AccountId>,
296 agent: Agent<Self::AccountId>,
297 amount: Self::Balance,
298 ) -> DispatchResult {
299 let delegator = delegator.get();
300 let mut delegators = DelegatorBalanceMap::get();
301 delegators.entry(delegator).and_modify(|b| *b += amount).or_insert(amount);
302 DelegatorBalanceMap::set(&delegators);
303
304 let agent = agent.get();
305 let mut agents = AgentBalanceMap::get();
306 agents
307 .get_mut(&agent)
308 .map(|(d, _, _)| *d += amount)
309 .ok_or(DispatchError::Other("agent not registered"))?;
310 AgentBalanceMap::set(&agents);
311
312 if BondedBalanceMap::get().contains_key(&agent) {
313 StakingMock::bond_extra(&agent, amount)
314 } else {
315 StakingMock::bond(&agent, amount, &999)
317 }
318 }
319
320 fn withdraw_delegation(
321 delegator: Delegator<Self::AccountId>,
322 agent: Agent<Self::AccountId>,
323 amount: Self::Balance,
324 _num_slashing_spans: u32,
325 ) -> DispatchResult {
326 let mut delegators = DelegatorBalanceMap::get();
327 delegators.get_mut(&delegator.get()).map(|b| *b -= amount);
328 DelegatorBalanceMap::set(&delegators);
329
330 let mut agents = AgentBalanceMap::get();
331 agents.get_mut(&agent.get()).map(|(d, u, _)| {
332 *d -= amount;
333 *u -= amount;
334 });
335 AgentBalanceMap::set(&agents);
336
337 Ok(())
338 }
339
340 fn pending_slash(agent: Agent<Self::AccountId>) -> Option<Self::Balance> {
341 AgentBalanceMap::get()
342 .get(&agent.get())
343 .copied()
344 .map(|(_, _, pending_slash)| pending_slash)
345 }
346
347 fn delegator_slash(
348 agent: Agent<Self::AccountId>,
349 delegator: Delegator<Self::AccountId>,
350 value: Self::Balance,
351 _maybe_reporter: Option<Self::AccountId>,
352 ) -> DispatchResult {
353 let mut delegators = DelegatorBalanceMap::get();
354 delegators.get_mut(&delegator.get()).map(|b| *b -= value);
355 DelegatorBalanceMap::set(&delegators);
356
357 let mut agents = AgentBalanceMap::get();
358 agents.get_mut(&agent.get()).map(|(_, _, p)| {
359 p.saturating_reduce(value);
360 });
361 AgentBalanceMap::set(&agents);
362
363 Ok(())
364 }
365}
366
367impl DelegateMock {
368 pub fn set_agent_balance(who: AccountId, delegated: Balance) {
369 let mut agents = AgentBalanceMap::get();
370 agents.insert(who, (delegated, 0, 0));
371 AgentBalanceMap::set(&agents);
372 }
373
374 pub fn set_delegator_balance(who: AccountId, amount: Balance) {
375 let mut delegators = DelegatorBalanceMap::get();
376 delegators.insert(who, amount);
377 DelegatorBalanceMap::set(&delegators);
378 }
379
380 pub fn on_slash(agent: AccountId, amount: Balance) {
381 let mut agents = AgentBalanceMap::get();
382 agents.get_mut(&agent).map(|(_, _, p)| *p += amount);
383 AgentBalanceMap::set(&agents);
384 }
385
386 fn on_withdraw(agent: AccountId, amount: Balance) {
387 let mut agents = AgentBalanceMap::get();
388 agents.get_mut(&agent).map(|(_, u, _)| *u += amount);
390 AgentBalanceMap::set(&agents);
391 }
392}
393
394impl DelegationMigrator for DelegateMock {
395 type Balance = Balance;
396 type AccountId = AccountId;
397 fn migrate_nominator_to_agent(
398 _agent: Agent<Self::AccountId>,
399 _reward_account: &Self::AccountId,
400 ) -> DispatchResult {
401 unimplemented!("not used in current unit tests")
402 }
403
404 fn migrate_delegation(
405 _agent: Agent<Self::AccountId>,
406 _delegator: Delegator<Self::AccountId>,
407 _value: Self::Balance,
408 ) -> DispatchResult {
409 unimplemented!("not used in current unit tests")
410 }
411
412 #[cfg(feature = "runtime-benchmarks")]
413 fn force_kill_agent(_agent: Agent<Self::AccountId>) {
414 unimplemented!("not used in current unit tests")
415 }
416}
417
418#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
419impl frame_system::Config for Runtime {
420 type Nonce = u64;
421 type AccountId = AccountId;
422 type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
423 type Block = Block;
424 type AccountData = pallet_balances::AccountData<Balance>;
425}
426
427parameter_types! {
428 pub static ExistentialDeposit: Balance = 5;
429}
430
431#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
432impl pallet_balances::Config for Runtime {
433 type Balance = Balance;
434 type ExistentialDeposit = ExistentialDeposit;
435 type AccountStore = System;
436 type FreezeIdentifier = RuntimeFreezeReason;
437 type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
438 type RuntimeFreezeReason = RuntimeFreezeReason;
439}
440
441pub struct BalanceToU256;
442impl Convert<Balance, U256> for BalanceToU256 {
443 fn convert(n: Balance) -> U256 {
444 n.into()
445 }
446}
447
448pub struct U256ToBalance;
449impl Convert<U256, Balance> for U256ToBalance {
450 fn convert(n: U256) -> Balance {
451 n.try_into().unwrap()
452 }
453}
454
455pub struct RestrictMock;
456impl Contains<AccountId> for RestrictMock {
457 fn contains(who: &AccountId) -> bool {
458 RestrictedAccounts::get().contains(who)
459 }
460}
461
462parameter_types! {
463 pub static PostUnbondingPoolsWindow: u32 = 2;
464 pub static MaxMetadataLen: u32 = 2;
465 pub static CheckLevel: u8 = 255;
466 pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
467}
468
469ord_parameter_types! {
470 pub const Admin: u128 = 42;
471}
472
473impl pools::Config for Runtime {
474 type RuntimeEvent = RuntimeEvent;
475 type WeightInfo = ();
476 type Currency = Balances;
477 type RuntimeFreezeReason = RuntimeFreezeReason;
478 type RewardCounter = RewardCounter;
479 type BalanceToU256 = BalanceToU256;
480 type U256ToBalance = U256ToBalance;
481 type StakeAdapter = adapter::DelegateStake<Self, StakingMock, DelegateMock>;
482 type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
483 type PalletId = PoolsPalletId;
484 type MaxMetadataLen = MaxMetadataLen;
485 type MaxUnbonding = MaxUnbonding;
486 type MaxPointsToBalance = frame_support::traits::ConstU8<10>;
487 type AdminOrigin = EnsureSignedBy<Admin, AccountId>;
488 type BlockNumberProvider = System;
489 type Filter = RestrictMock;
490}
491
492type Block = frame_system::mocking::MockBlock<Runtime>;
493frame_support::construct_runtime!(
494 pub enum Runtime {
495 System: frame_system,
496 Balances: pallet_balances,
497 Pools: pools,
498 }
499);
500
501pub struct ExtBuilder {
502 members: Vec<(AccountId, Balance)>,
503 max_members: Option<u32>,
504 max_members_per_pool: Option<u32>,
505 global_max_commission: Option<Perbill>,
506}
507
508impl Default for ExtBuilder {
509 fn default() -> Self {
510 Self {
511 members: Default::default(),
512 max_members: Some(4),
513 max_members_per_pool: Some(3),
514 global_max_commission: Some(Perbill::from_percent(90)),
515 }
516 }
517}
518
519#[cfg_attr(feature = "fuzzing", allow(dead_code))]
520impl ExtBuilder {
521 pub fn add_members(mut self, members: Vec<(AccountId, Balance)>) -> Self {
523 self.members = members;
524 self
525 }
526
527 pub fn ed(self, ed: Balance) -> Self {
528 ExistentialDeposit::set(ed);
529 self
530 }
531
532 pub fn min_bond(self, min: Balance) -> Self {
533 StakingMinBond::set(min);
534 self
535 }
536
537 pub fn min_join_bond(self, min: Balance) -> Self {
538 MinJoinBondConfig::set(min);
539 self
540 }
541
542 pub fn with_check(self, level: u8) -> Self {
543 CheckLevel::set(level);
544 self
545 }
546
547 pub fn max_members(mut self, max: Option<u32>) -> Self {
548 self.max_members = max;
549 self
550 }
551
552 pub fn max_members_per_pool(mut self, max: Option<u32>) -> Self {
553 self.max_members_per_pool = max;
554 self
555 }
556
557 pub fn global_max_commission(mut self, commission: Option<Perbill>) -> Self {
558 self.global_max_commission = commission;
559 self
560 }
561
562 pub fn build(self) -> sp_io::TestExternalities {
563 sp_tracing::try_init_simple();
564 let mut storage =
565 frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
566
567 let _ = crate::GenesisConfig::<Runtime> {
568 min_join_bond: MinJoinBondConfig::get(),
569 min_create_bond: 2,
570 max_pools: Some(2),
571 max_members_per_pool: self.max_members_per_pool,
572 max_members: self.max_members,
573 global_max_commission: self.global_max_commission,
574 }
575 .assimilate_storage(&mut storage);
576
577 let mut ext = sp_io::TestExternalities::from(storage);
578
579 ext.execute_with(|| {
580 frame_system::Pallet::<Runtime>::set_block_number(1);
582
583 let amount_to_bond = Pools::depositor_min_bond();
585 Currency::set_balance(&10, amount_to_bond * 5);
586 assert_ok!(Pools::create(RawOrigin::Signed(10).into(), amount_to_bond, 900, 901, 902));
587 assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1]));
588 let last_pool = LastPoolId::<Runtime>::get();
589 for (account_id, bonded) in self.members {
590 <Runtime as Config>::Currency::set_balance(&account_id, bonded * 2);
591 assert_ok!(Pools::join(RawOrigin::Signed(account_id).into(), bonded, last_pool));
592 }
593 });
594
595 ext
596 }
597
598 pub fn build_and_execute(self, test: impl FnOnce()) {
599 self.build().execute_with(|| {
600 test();
601 Pools::do_try_state(CheckLevel::get()).unwrap();
602 })
603 }
604}
605
606pub fn unsafe_set_state(pool_id: PoolId, state: PoolState) {
607 BondedPools::<Runtime>::try_mutate(pool_id, |maybe_bonded_pool| {
608 maybe_bonded_pool.as_mut().ok_or(()).map(|bonded_pool| {
609 bonded_pool.state = state;
610 })
611 })
612 .unwrap()
613}
614
615parameter_types! {
616 storage PoolsEvents: u32 = 0;
617 storage BalancesEvents: u32 = 0;
618}
619
620pub fn run_blocks(n: u64) {
622 let current_block = System::block_number();
623 System::run_to_block::<AllPalletsWithSystem>(n + current_block);
624}
625
626pub fn pool_events_since_last_call() -> Vec<super::Event<Runtime>> {
628 let events = System::events()
629 .into_iter()
630 .map(|r| r.event)
631 .filter_map(|e| if let RuntimeEvent::Pools(inner) = e { Some(inner) } else { None })
632 .collect::<Vec<_>>();
633 let already_seen = PoolsEvents::get();
634 PoolsEvents::set(&(events.len() as u32));
635 events.into_iter().skip(already_seen as usize).collect()
636}
637
638pub fn balances_events_since_last_call() -> Vec<pallet_balances::Event<Runtime>> {
640 let events = System::events()
641 .into_iter()
642 .map(|r| r.event)
643 .filter_map(|e| if let RuntimeEvent::Balances(inner) = e { Some(inner) } else { None })
644 .collect::<Vec<_>>();
645 let already_seen = BalancesEvents::get();
646 BalancesEvents::set(&(events.len() as u32));
647 events.into_iter().skip(already_seen as usize).collect()
648}
649
650pub fn fully_unbond_permissioned(member: AccountId) -> DispatchResult {
652 let points = PoolMembers::<Runtime>::get(member)
653 .map(|d| d.active_points())
654 .unwrap_or_default();
655 Pools::unbond(RuntimeOrigin::signed(member), member, points)
656}
657
658pub fn pending_rewards_for_delegator(delegator: AccountId) -> Balance {
659 let member = PoolMembers::<T>::get(delegator).unwrap();
660 let bonded_pool = BondedPools::<T>::get(member.pool_id).unwrap();
661 let reward_pool = RewardPools::<T>::get(member.pool_id).unwrap();
662
663 assert!(!bonded_pool.points.is_zero());
664
665 let commission = bonded_pool.commission.current();
666 let current_rc = reward_pool
667 .current_reward_counter(member.pool_id, bonded_pool.points, commission)
668 .unwrap()
669 .0;
670
671 member.pending_rewards(current_rc).unwrap_or_default()
672}
673
674#[derive(PartialEq, Debug)]
675pub enum RewardImbalance {
676 Surplus(Balance),
678 Deficit(Balance),
680}
681
682pub fn pool_pending_rewards(pool: PoolId) -> Result<BalanceOf<T>, sp_runtime::DispatchError> {
683 let bonded_pool = BondedPools::<T>::get(pool).ok_or(Error::<T>::PoolNotFound)?;
684 let reward_pool = RewardPools::<T>::get(pool).ok_or(Error::<T>::PoolNotFound)?;
685
686 let current_rc = if !bonded_pool.points.is_zero() {
687 let commission = bonded_pool.commission.current();
688 reward_pool.current_reward_counter(pool, bonded_pool.points, commission)?.0
689 } else {
690 Default::default()
691 };
692
693 Ok(PoolMembers::<T>::iter()
694 .filter(|(_, d)| d.pool_id == pool)
695 .map(|(_, d)| d.pending_rewards(current_rc).unwrap_or_default())
696 .fold(0u32.into(), |acc: BalanceOf<T>, x| acc.saturating_add(x)))
697}
698
699pub fn reward_imbalance(pool: PoolId) -> RewardImbalance {
700 let pending_rewards = pool_pending_rewards(pool).expect("pool should exist");
701 let current_balance = RewardPool::<Runtime>::current_balance(pool);
702
703 if pending_rewards > current_balance {
704 RewardImbalance::Deficit(pending_rewards - current_balance)
705 } else {
706 RewardImbalance::Surplus(current_balance - pending_rewards)
707 }
708}
709
710pub fn set_pool_balance(who: AccountId, amount: Balance) {
711 StakingMock::set_bonded_balance(who, amount);
712 DelegateMock::set_agent_balance(who, amount);
713}
714
715pub fn member_delegation(who: AccountId) -> Balance {
716 <T as Config>::StakeAdapter::member_delegation_balance(Member::from(who))
717 .expect("who must be a pool member")
718}
719
720pub fn pool_balance(id: PoolId) -> Balance {
721 <T as Config>::StakeAdapter::total_balance(Pool::from(Pools::generate_bonded_account(id)))
722 .expect("who must be a bonded pool account")
723}
724
725pub fn add_to_restrict_list(who: &AccountId) {
726 if !RestrictedAccounts::get().contains(who) {
727 RestrictedAccounts::mutate(|l| l.push(*who));
728 }
729}
730
731pub fn remove_from_restrict_list(who: &AccountId) {
732 RestrictedAccounts::mutate(|l| l.retain(|x| x != who));
733}
734
735#[cfg(test)]
736mod test {
737 use super::*;
738 #[test]
739 fn u256_to_balance_convert_works() {
740 assert_eq!(U256ToBalance::convert(0u32.into()), Zero::zero());
741 assert_eq!(U256ToBalance::convert(Balance::max_value().into()), Balance::max_value())
742 }
743
744 #[test]
745 #[should_panic]
746 fn u256_to_balance_convert_panics_correctly() {
747 U256ToBalance::convert(U256::from(Balance::max_value()).saturating_add(1u32.into()));
748 }
749
750 #[test]
751 fn balance_to_u256_convert_works() {
752 assert_eq!(BalanceToU256::convert(0u32.into()), U256::zero());
753 assert_eq!(BalanceToU256::convert(Balance::max_value()), Balance::max_value().into())
754 }
755}