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