referrerpolicy=no-referrer-when-downgrade

pallet_nomination_pools/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Nomination Pools for Staking Delegation
19//!
20//! A pallet that allows members to delegate their stake to nominating pools. A nomination pool acts
21//! as nominator and nominates validators on the members' behalf.
22//!
23//! # Index
24//!
25//! * [Key terms](#key-terms)
26//! * [Usage](#usage)
27//! * [Implementor's Guide](#implementors-guide)
28//! * [Design](#design)
29//!
30//! ## Key Terms
31//!
32//!  * pool id: A unique identifier of each pool. Set to u32.
33//!  * bonded pool: Tracks the distribution of actively staked funds. See [`BondedPool`] and
34//! [`BondedPoolInner`].
35//! * reward pool: Tracks rewards earned by actively staked funds. See [`RewardPool`] and
36//!   [`RewardPools`].
37//! * unbonding sub pools: Collection of pools at different phases of the unbonding lifecycle. See
38//!   [`SubPools`] and [`SubPoolsStorage`].
39//! * members: Accounts that are members of pools. See [`PoolMember`] and [`PoolMembers`].
40//! * roles: Administrative roles of each pool, capable of controlling nomination, and the state of
41//!   the pool.
42//! * point: A unit of measure for a members portion of a pool's funds. Points initially have a
43//!   ratio of 1 (as set by `POINTS_TO_BALANCE_INIT_RATIO`) to balance, but as slashing happens,
44//!   this can change.
45//! * kick: The act of a pool administrator forcibly ejecting a member.
46//! * bonded account: A key-less account id derived from the pool id that acts as the bonded
47//!   account. This account registers itself as a nominator in the staking system, and follows
48//!   exactly the same rules and conditions as a normal staker. Its bond increases or decreases as
49//!   members join, it can `nominate` or `chill`, and might not even earn staking rewards if it is
50//!   not nominating proper validators.
51//! * reward account: A similar key-less account, that is set as the `Payee` account for the bonded
52//!   account for all staking rewards.
53//! * change rate: The rate at which pool commission can be changed. A change rate consists of a
54//!   `max_increase` and `min_delay`, dictating the maximum percentage increase that can be applied
55//!   to the commission per number of blocks.
56//! * throttle: An attempted commission increase is throttled if the attempted change falls outside
57//!   the change rate bounds.
58//!
59//! ## Usage
60//!
61//! ### Join
62//!
63//! An account can stake funds with a nomination pool by calling [`Call::join`].
64//!
65//! ### Claim rewards
66//!
67//! After joining a pool, a member can claim rewards by calling [`Call::claim_payout`].
68//!
69//! A pool member can also set a `ClaimPermission` with [`Call::set_claim_permission`], to allow
70//! other members to permissionlessly bond or withdraw their rewards by calling
71//! [`Call::bond_extra_other`] or [`Call::claim_payout_other`] respectively.
72//!
73//! For design docs see the [reward pool](#reward-pool) section.
74//!
75//! ### Leave
76//!
77//! In order to leave, a member must take two steps.
78//!
79//! First, they must call [`Call::unbond`]. The unbond extrinsic will start the unbonding process by
80//! unbonding all or a portion of the members funds.
81//!
82//! > A member can have up to [`Config::MaxUnbonding`] distinct active unbonding requests.
83//!
84//! Second, once [`sp_staking::StakingInterface::bonding_duration`] eras have passed, the member can
85//! call [`Call::withdraw_unbonded`] to withdraw any funds that are free.
86//!
87//! For design docs see the [bonded pool](#bonded-pool) and [unbonding sub
88//! pools](#unbonding-sub-pools) sections.
89//!
90//! ### Slashes
91//!
92//! Slashes are distributed evenly across the bonded pool and the unbonding pools from slash era+1
93//! through the slash apply era. Thus, any member who either
94//!
95//! 1. unbonded, or
96//! 2. was actively bonded
97//
98//! in the aforementioned range of eras will be affected by the slash. A member is slashed pro-rata
99//! based on its stake relative to the total slash amount.
100//!
101//! Slashing does not change any single member's balance. Instead, the slash will only reduce the
102//! balance associated with a particular pool. But, we never change the total *points* of a pool
103//! because of slashing. Therefore, when a slash happens, the ratio of points to balance changes in
104//! a pool. In other words, the value of one point, which is initially 1-to-1 against a unit of
105//! balance, is now less than one balance because of the slash.
106//!
107//! ### Administration
108//!
109//! A pool can be created with the [`Call::create`] call. Once created, the pools nominator or root
110//! user must call [`Call::nominate`] to start nominating. [`Call::nominate`] can be called at
111//! anytime to update validator selection.
112//!
113//! Similar to [`Call::nominate`], [`Call::chill`] will chill to pool in the staking system, and
114//! [`Call::pool_withdraw_unbonded`] will withdraw any unbonding chunks of the pool bonded account.
115//! The latter call is permissionless and can be called by anyone at any time.
116//!
117//! To help facilitate pool administration the pool has one of three states (see [`PoolState`]):
118//!
119//! * Open: Anyone can join the pool and no members can be permissionlessly removed.
120//! * Blocked: No members can join and some admin roles can kick members. Kicking is not instant,
121//!   and follows the same process of `unbond` and then `withdraw_unbonded`. In other words,
122//!   administrators can permissionlessly unbond other members.
123//! * Destroying: No members can join and all members can be permissionlessly removed with
124//!   [`Call::unbond`] and [`Call::withdraw_unbonded`]. Once a pool is in destroying state, it
125//!   cannot be reverted to another state.
126//!
127//! A pool has 4 administrative roles (see [`PoolRoles`]):
128//!
129//! * Depositor: creates the pool and is the initial member. They can only leave the pool once all
130//!   other members have left. Once they fully withdraw their funds, the pool is destroyed.
131//! * Nominator: can select which validators the pool nominates.
132//! * Bouncer: can change the pools state and kick members if the pool is blocked.
133//! * Root: can change the nominator, bouncer, or itself, manage and claim commission, and can
134//!   perform any of the actions the nominator or bouncer can.
135//!
136//! ### Commission
137//!
138//! A pool can optionally have a commission configuration, via the `root` role, set with
139//! [`Call::set_commission`] and claimed with [`Call::claim_commission`]. A payee account must be
140//! supplied with the desired commission percentage. Beyond the commission itself, a pool can have a
141//! maximum commission and a change rate.
142//!
143//! Importantly, both max commission  [`Call::set_commission_max`] and change rate
144//! [`Call::set_commission_change_rate`] can not be removed once set, and can only be set to more
145//! restrictive values (i.e. a lower max commission or a slower change rate) in subsequent updates.
146//!
147//! If set, a pool's commission is bound to [`GlobalMaxCommission`] at the time it is applied to
148//! pending rewards. [`GlobalMaxCommission`] is intended to be updated only via governance.
149//!
150//! When a pool is dissolved, any outstanding pending commission that has not been claimed will be
151//! transferred to the depositor.
152//!
153//! Implementation note: Commission is analogous to a separate member account of the pool, with its
154//! own reward counter in the form of `current_pending_commission`.
155//!
156//! Crucially, commission is applied to rewards based on the current commission in effect at the
157//! time rewards are transferred into the reward pool. This is to prevent the malicious behaviour of
158//! changing the commission rate to a very high value after rewards are accumulated, and thus claim
159//! an unexpectedly high chunk of the reward.
160//!
161//! ### Dismantling
162//!
163//! As noted, a pool is destroyed once
164//!
165//! 1. First, all members need to fully unbond and withdraw. If the pool state is set to
166//!    `Destroying`, this can happen permissionlessly.
167//! 2. The depositor itself fully unbonds and withdraws.
168//!
169//! > Note that at this point, based on the requirements of the staking system, the pool's bonded
170//! > account's stake might not be able to ge below a certain threshold as a nominator. At this
171//! > point, the pool should `chill` itself to allow the depositor to leave. See [`Call::chill`].
172//!
173//! ## Implementor's Guide
174//!
175//! Some notes and common mistakes that wallets/apps wishing to implement this pallet should be
176//! aware of:
177//!
178//!
179//! ### Pool Members
180//!
181//! * In general, whenever a pool member changes their total points, the chain will automatically
182//!   claim all their pending rewards for them. This is not optional, and MUST happen for the reward
183//!   calculation to remain correct (see the documentation of `bond` as an example). So, make sure
184//!   you are warning your users about it. They might be surprised if they see that they bonded an
185//!   extra 100 DOTs, and now suddenly their 5.23 DOTs in pending reward is gone. It is not gone, it
186//!   has been paid out to you!
187//! * Joining a pool implies transferring funds to the pool account. So it might be (based on which
188//!   wallet that you are using) that you no longer see the funds that are moved to the pool in your
189//!   “free balance” section. Make sure the user is aware of this, and not surprised by seeing this.
190//!   Also, the transfer that happens here is configured to to never accidentally destroy the sender
191//!   account. So to join a Pool, your sender account must remain alive with 1 DOT left in it. This
192//!   means, with 1 DOT as existential deposit, and 1 DOT as minimum to join a pool, you need at
193//!   least 2 DOT to join a pool. Consequently, if you are suggesting members to join a pool with
194//!   “Maximum possible value”, you must subtract 1 DOT to remain in the sender account to not
195//!   accidentally kill it.
196//! * Points and balance are not the same! Any pool member, at any point in time, can have points in
197//!   either the bonded pool or any of the unbonding pools. The crucial fact is that in any of these
198//!   pools, the ratio of point to balance is different and might not be 1. Each pool starts with a
199//!   ratio of 1, but as time goes on, for reasons such as slashing, the ratio gets broken. Over
200//!   time, 100 points in a bonded pool can be worth 90 DOTs. Make sure you are either representing
201//!   points as points (not as DOTs), or even better, always display both: “You have x points in
202//!   pool y which is worth z DOTs”. See here and here for examples of how to calculate point to
203//!   balance ratio of each pool (it is almost trivial ;))
204//!
205//! ### Pool Management
206//!
207//! * The pool will be seen from the perspective of the rest of the system as a single nominator.
208//!   Ergo, This nominator must always respect the `staking.minNominatorBond` limit. Similar to a
209//!   normal nominator, who has to first `chill` before fully unbonding, the pool must also do the
210//!   same. The pool’s bonded account will be fully unbonded only when the depositor wants to leave
211//!   and dismantle the pool. All that said, the message is: the depositor can only leave the chain
212//!   when they chill the pool first.
213//!
214//! ## Design
215//!
216//! _Notes_: this section uses pseudo code to explain general design and does not necessarily
217//! reflect the exact implementation. Additionally, a working knowledge of `pallet-staking`'s api is
218//! assumed.
219//!
220//! ### Goals
221//!
222//! * Maintain network security by upholding integrity of slashing events, sufficiently penalizing
223//!   members that where in the pool while it was backing a validator that got slashed.
224//! * Maximize scalability in terms of member count.
225//!
226//! In order to maintain scalability, all operations are independent of the number of members. To do
227//! this, delegation specific information is stored local to the member while the pool data
228//! structures have bounded datum.
229//!
230//! ### Bonded pool
231//!
232//! A bonded pool nominates with its total balance, excluding that which has been withdrawn for
233//! unbonding. The total points of a bonded pool are always equal to the sum of points of the
234//! delegation members. A bonded pool tracks its points and reads its bonded balance.
235//!
236//! When a member joins a pool, `amount_transferred` is transferred from the members account to the
237//! bonded pools account. Then the pool calls `staking::bond_extra(amount_transferred)` and issues
238//! new points which are tracked by the member and added to the bonded pool's points.
239//!
240//! When the pool already has some balance, we want the value of a point before the transfer to
241//! equal the value of a point after the transfer. So, when a member joins a bonded pool with a
242//! given `amount_transferred`, we maintain the ratio of bonded balance to points such that:
243//!
244//! ```text
245//! balance_after_transfer / points_after_transfer == balance_before_transfer / points_before_transfer;
246//! ```
247//!
248//! To achieve this, we issue points based on the following:
249//!
250//! ```text
251//! points_issued = (points_before_transfer / balance_before_transfer) * amount_transferred;
252//! ```
253//!
254//! For new bonded pools we can set the points issued per balance arbitrarily. In this
255//! implementation we use a 1 points to 1 balance ratio for pool creation (see
256//! [`POINTS_TO_BALANCE_INIT_RATIO`]).
257//!
258//! **Relevant extrinsics:**
259//!
260//! * [`Call::create`]
261//! * [`Call::join`]
262//!
263//! ### Reward pool
264//!
265//! When a pool is first bonded it sets up a deterministic, inaccessible account as its reward
266//! destination. This reward account combined with `RewardPool` compose a reward pool.
267//!
268//! Reward pools are completely separate entities to bonded pools. Along with its account, a reward
269//! pool also tracks its outstanding and claimed rewards as counters, in addition to pending and
270//! claimed commission. These counters are updated with `RewardPool::update_records`. The current
271//! reward counter of the pool (the total outstanding rewards, in points) is also callable with the
272//! `RewardPool::current_reward_counter` method.
273//!
274//! See [this link](https://hackmd.io/PFGn6wI5TbCmBYoEA_f2Uw) for an in-depth explanation of the
275//! reward pool mechanism.
276//!
277//! **Relevant extrinsics:**
278//!
279//! * [`Call::claim_payout`]
280//!
281//! ### Unbonding sub pools
282//!
283//! When a member unbonds, it's balance is unbonded in the bonded pool's account and tracked in an
284//! unbonding pool associated with the active era. If no such pool exists, one is created. To track
285//! which unbonding sub pool a member belongs too, a member tracks it's `unbonding_era`.
286//!
287//! When a member initiates unbonding it's claim on the bonded pool (`balance_to_unbond`) is
288//! computed as:
289//!
290//! ```text
291//! balance_to_unbond = (bonded_pool.balance / bonded_pool.points) * member.points;
292//! ```
293//!
294//! If this is the first transfer into an unbonding pool arbitrary amount of points can be issued
295//! per balance. In this implementation unbonding pools are initialized with a 1 point to 1 balance
296//! ratio (see [`POINTS_TO_BALANCE_INIT_RATIO`]). Otherwise, the unbonding pools hold the same
297//! points to balance ratio properties as the bonded pool, so member points in the unbonding pool
298//! are issued based on
299//!
300//! ```text
301//! new_points_issued = (points_before_transfer / balance_before_transfer) * balance_to_unbond;
302//! ```
303//!
304//! For scalability, a bound is maintained on the number of unbonding sub pools (see
305//! [`Config::MaxUnbondingPools`]). An unbonding pool is removed (merged into the unbonded pool)
306//! once it is older than `active_era - (MaxUnbondingPools - bonding_duration)`. An
307//! unbonding pool is merged into the unbonded pool with
308//!
309//! ```text
310//! unbounded_pool.balance = unbounded_pool.balance + unbonding_pool.balance;
311//! unbounded_pool.points = unbounded_pool.points + unbonding_pool.points;
312//! ```
313//!
314//! This scheme "averages" out the points value in the unbonded pool.
315//!
316//! Once a members `unbonding_era` is older than `active_era -
317//! [sp_staking::StakingInterface::bonding_duration]`, it can can cash it's points out of the
318//! corresponding unbonding pool. If it's `unbonding_era` is older than the effective
319//! post-unbonding window, it can cash it's points from the unbonded pool.
320//!
321//! **Relevant extrinsics:**
322//!
323//! * [`Call::unbond`]
324//! * [`Call::withdraw_unbonded`]
325//!
326//! ### Slashing
327//!
328//! This section assumes that the slash computation is executed by
329//! `pallet_staking::StakingLedger::slash`, which passes the information to this pallet via
330//! [`sp_staking::OnStakingUpdate::on_slash`].
331//!
332//! Unbonding pools need to be slashed to ensure all nominators whom where in the bonded pool while
333//! it was backing a validator that equivocated are punished. Without these measures a member could
334//! unbond right after a validator equivocated with no consequences.
335//!
336//! This strategy is unfair to members who joined after the slash, because they get slashed as well,
337//! but spares members who unbond. The latter is much more important for security: if a pool's
338//! validators are attacking the network, their members need to unbond fast! Avoiding slashes gives
339//! them an incentive to do that if validators get repeatedly slashed.
340//!
341//! To be fair to joiners, this implementation also need joining pools, which are actively staking,
342//! in addition to the unbonding pools. For maintenance simplicity these are not implemented.
343//! Related: <https://github.com/paritytech/substrate/issues/10860>
344//!
345//! ### Limitations
346//!
347//! * PoolMembers cannot vote with their staked funds because they are transferred into the pools
348//!   account. In the future this can be overcome by allowing the members to vote with their bonded
349//!   funds via vote splitting.
350//! * PoolMembers cannot quickly transfer to another pool if they do no like nominations, instead
351//!   they must wait for the unbonding duration.
352
353#![cfg_attr(not(feature = "std"), no_std)]
354
355extern crate alloc;
356
357use adapter::{Member, Pool, StakeStrategy};
358use alloc::{collections::btree_map::BTreeMap, vec::Vec};
359use codec::{Codec, DecodeWithMemTracking};
360use core::{fmt::Debug, ops::Div};
361use frame_support::{
362	defensive, defensive_assert, ensure,
363	pallet_prelude::{MaxEncodedLen, *},
364	storage::bounded_btree_map::BoundedBTreeMap,
365	traits::{
366		fungible::{Inspect, InspectFreeze, Mutate, MutateFreeze},
367		tokens::{Fortitude, Preservation},
368		Contains, Defensive, DefensiveOption, DefensiveResult, DefensiveSaturating, Get,
369	},
370	DefaultNoBound, PalletError,
371};
372use scale_info::TypeInfo;
373use sp_core::U256;
374use sp_runtime::{
375	traits::{
376		AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup,
377		Zero,
378	},
379	FixedPointNumber, Perbill,
380};
381use sp_staking::{EraIndex, StakingInterface};
382
383#[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))]
384use sp_runtime::TryRuntimeError;
385
386/// The log target of this pallet.
387pub const LOG_TARGET: &str = "runtime::nomination-pools";
388// syntactic sugar for logging.
389#[macro_export]
390macro_rules! log {
391	($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
392		log::$level!(
393			target: $crate::LOG_TARGET,
394			concat!("[{:?}] 🏊‍♂️ ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)*
395		)
396	};
397}
398
399#[cfg(any(test, feature = "fuzzing"))]
400pub mod mock;
401#[cfg(test)]
402mod tests;
403
404pub mod adapter;
405pub mod migration;
406pub mod weights;
407
408pub use pallet::*;
409use sp_runtime::traits::BlockNumberProvider;
410pub use weights::WeightInfo;
411
412/// The balance type used by the currency system.
413pub type BalanceOf<T> =
414	<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
415/// Type used for unique identifier of each pool.
416pub type PoolId = u32;
417
418type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
419
420pub type BlockNumberFor<T> =
421	<<T as Config>::BlockNumberProvider as BlockNumberProvider>::BlockNumber;
422
423pub const POINTS_TO_BALANCE_INIT_RATIO: u32 = 1;
424
425/// Possible operations on the configuration values of this pallet.
426#[derive(
427	Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone,
428)]
429pub enum ConfigOp<T: Codec + Debug> {
430	/// Don't change.
431	Noop,
432	/// Set the given value.
433	Set(T),
434	/// Remove from storage.
435	Remove,
436}
437
438/// The type of bonding that can happen to a pool.
439pub enum BondType {
440	/// Someone is bonding into the pool upon creation.
441	Create,
442	/// Someone is adding more funds later to this pool.
443	Extra,
444}
445
446/// How to increase the bond of a member.
447#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)]
448pub enum BondExtra<Balance> {
449	/// Take from the free balance.
450	FreeBalance(Balance),
451	/// Take the entire amount from the accumulated rewards.
452	Rewards,
453}
454
455/// The type of account being created.
456#[derive(Encode, Decode)]
457enum AccountType {
458	Bonded,
459	Reward,
460}
461
462/// The permission a pool member can set for other accounts to claim rewards on their behalf.
463#[derive(
464	Encode,
465	Decode,
466	DecodeWithMemTracking,
467	MaxEncodedLen,
468	Clone,
469	Copy,
470	Debug,
471	PartialEq,
472	Eq,
473	TypeInfo,
474)]
475pub enum ClaimPermission {
476	/// Only the pool member themselves can claim their rewards.
477	Permissioned,
478	/// Anyone can compound rewards on a pool member's behalf.
479	PermissionlessCompound,
480	/// Anyone can withdraw rewards on a pool member's behalf.
481	PermissionlessWithdraw,
482	/// Anyone can withdraw and compound rewards on a pool member's behalf.
483	PermissionlessAll,
484}
485
486impl Default for ClaimPermission {
487	fn default() -> Self {
488		Self::PermissionlessWithdraw
489	}
490}
491
492impl ClaimPermission {
493	/// Permissionless compounding of pool rewards is allowed if the current permission is
494	/// `PermissionlessCompound`, or permissionless.
495	fn can_bond_extra(&self) -> bool {
496		matches!(self, ClaimPermission::PermissionlessAll | ClaimPermission::PermissionlessCompound)
497	}
498
499	/// Permissionless payout claiming is allowed if the current permission is
500	/// `PermissionlessWithdraw`, or permissionless.
501	fn can_claim_payout(&self) -> bool {
502		matches!(self, ClaimPermission::PermissionlessAll | ClaimPermission::PermissionlessWithdraw)
503	}
504}
505
506/// A member in a pool.
507#[derive(
508	Encode,
509	Decode,
510	DecodeWithMemTracking,
511	MaxEncodedLen,
512	TypeInfo,
513	DebugNoBound,
514	CloneNoBound,
515	PartialEqNoBound,
516	EqNoBound,
517)]
518#[cfg_attr(feature = "std", derive(DefaultNoBound))]
519#[scale_info(skip_type_params(T))]
520pub struct PoolMember<T: Config> {
521	/// The identifier of the pool to which `who` belongs.
522	pub pool_id: PoolId,
523	/// The quantity of points this member has in the bonded pool or in a sub pool if
524	/// `Self::unbonding_era` is some.
525	pub points: BalanceOf<T>,
526	/// The reward counter at the time of this member's last payout claim.
527	pub last_recorded_reward_counter: T::RewardCounter,
528	/// The eras in which this member is unbonding, mapped from era index to the number of
529	/// points scheduled to unbond in the given era.
530	pub unbonding_eras: BoundedBTreeMap<EraIndex, BalanceOf<T>, T::MaxUnbonding>,
531}
532
533impl<T: Config> PoolMember<T> {
534	/// The pending rewards of this member.
535	fn pending_rewards(
536		&self,
537		current_reward_counter: T::RewardCounter,
538	) -> Result<BalanceOf<T>, Error<T>> {
539		// accuracy note: Reward counters are `FixedU128` with base of 10^18. This value is being
540		// multiplied by a point. The worse case of a point is 10x the granularity of the balance
541		// (10x is the common configuration of `MaxPointsToBalance`).
542		//
543		// Assuming roughly the current issuance of polkadot (12,047,781,394,999,601,455, which is
544		// 1.2 * 10^9 * 10^10 = 1.2 * 10^19), the worse case point value is around 10^20.
545		//
546		// The final multiplication is:
547		//
548		// rc * 10^20 / 10^18 = rc * 100
549		//
550		// the implementation of `multiply_by_rational_with_rounding` shows that it will only fail
551		// if the final division is not enough to fit in u128. In other words, if `rc * 100` is more
552		// than u128::max. Given that RC is interpreted as reward per unit of point, and unit of
553		// point is equal to balance (normally), and rewards are usually a proportion of the points
554		// in the pool, the likelihood of rc reaching near u128::MAX is near impossible.
555
556		(current_reward_counter.defensive_saturating_sub(self.last_recorded_reward_counter))
557			.checked_mul_int(self.active_points())
558			.ok_or(Error::<T>::OverflowRisk)
559	}
560
561	/// Active balance of the member.
562	///
563	/// This is derived from the ratio of points in the pool to which the member belongs to.
564	/// Might return different values based on the pool state for the same member and points.
565	fn active_balance(&self) -> BalanceOf<T> {
566		if let Some(pool) = BondedPool::<T>::get(self.pool_id).defensive() {
567			pool.points_to_balance(self.points)
568		} else {
569			Zero::zero()
570		}
571	}
572
573	/// Total balance of the member, both active and unbonding.
574	/// Doesn't mutate state.
575	///
576	/// Worst case, iterates over [`Config::MaxUnbondingPools`] member unbonding pools to
577	/// calculate member balance.
578	pub fn total_balance(&self) -> BalanceOf<T> {
579		let pool = match BondedPool::<T>::get(self.pool_id) {
580			Some(pool) => pool,
581			None => {
582				// this internal function is always called with a valid pool id.
583				defensive!("pool should exist; qed");
584				return Zero::zero();
585			},
586		};
587
588		let active_balance = pool.points_to_balance(self.active_points());
589
590		let sub_pools = match SubPoolsStorage::<T>::get(self.pool_id) {
591			Some(sub_pools) => sub_pools,
592			None => return active_balance,
593		};
594
595		let unbonding_balance = self.unbonding_eras.iter().fold(
596			BalanceOf::<T>::zero(),
597			|accumulator, (era, unlocked_points)| {
598				// if the `SubPools::with_era` has already been merged into the
599				// `SubPools::no_era` use this pool instead.
600				let era_pool = sub_pools.with_era.get(era).unwrap_or(&sub_pools.no_era);
601				accumulator + (era_pool.point_to_balance(*unlocked_points))
602			},
603		);
604
605		active_balance + unbonding_balance
606	}
607
608	/// Total points of this member, both active and unbonding.
609	fn total_points(&self) -> BalanceOf<T> {
610		self.active_points().saturating_add(self.unbonding_points())
611	}
612
613	/// Active points of the member.
614	fn active_points(&self) -> BalanceOf<T> {
615		self.points
616	}
617
618	/// Inactive points of the member, waiting to be withdrawn.
619	fn unbonding_points(&self) -> BalanceOf<T> {
620		self.unbonding_eras
621			.as_ref()
622			.iter()
623			.fold(BalanceOf::<T>::zero(), |acc, (_, v)| acc.saturating_add(*v))
624	}
625
626	/// Try and unbond `points_dissolved` from self, and in return mint `points_issued` into the
627	/// corresponding `era`'s unlock schedule.
628	///
629	/// In the absence of slashing, these two points are always the same. In the presence of
630	/// slashing, the value of points in different pools varies.
631	///
632	/// Returns `Ok(())` and updates `unbonding_eras` and `points` if success, `Err(_)` otherwise.
633	fn try_unbond(
634		&mut self,
635		points_dissolved: BalanceOf<T>,
636		points_issued: BalanceOf<T>,
637		unbonding_era: EraIndex,
638	) -> Result<(), Error<T>> {
639		if let Some(new_points) = self.points.checked_sub(&points_dissolved) {
640			match self.unbonding_eras.get_mut(&unbonding_era) {
641				Some(already_unbonding_points) => {
642					*already_unbonding_points =
643						already_unbonding_points.saturating_add(points_issued)
644				},
645				None => self
646					.unbonding_eras
647					.try_insert(unbonding_era, points_issued)
648					.map(|old| {
649						if old.is_some() {
650							defensive!("value checked to not exist in the map; qed");
651						}
652					})
653					.map_err(|_| Error::<T>::MaxUnbondingLimit)?,
654			}
655			self.points = new_points;
656			Ok(())
657		} else {
658			Err(Error::<T>::MinimumBondNotMet)
659		}
660	}
661
662	/// Withdraw any funds in [`Self::unbonding_eras`] who's deadline in reached and is fully
663	/// unlocked.
664	///
665	/// Returns a a subset of [`Self::unbonding_eras`] that got withdrawn.
666	///
667	/// Infallible, noop if no unbonding eras exist.
668	fn withdraw_unlocked(
669		&mut self,
670		active_era: EraIndex,
671	) -> BoundedBTreeMap<EraIndex, BalanceOf<T>, T::MaxUnbonding> {
672		// NOTE: if only drain-filter was stable..
673		let mut removed_points =
674			BoundedBTreeMap::<EraIndex, BalanceOf<T>, T::MaxUnbonding>::default();
675		self.unbonding_eras.retain(|e, p| {
676			if *e > active_era {
677				true
678			} else {
679				removed_points
680					.try_insert(*e, *p)
681					.expect("source map is bounded, this is a subset, will be bounded; qed");
682				false
683			}
684		});
685		removed_points
686	}
687}
688
689/// A pool's possible states.
690#[derive(
691	Encode,
692	Decode,
693	DecodeWithMemTracking,
694	MaxEncodedLen,
695	TypeInfo,
696	PartialEq,
697	DebugNoBound,
698	Clone,
699	Copy,
700)]
701pub enum PoolState {
702	/// The pool is open to be joined, and is working normally.
703	Open,
704	/// The pool is blocked. No one else can join.
705	Blocked,
706	/// The pool is in the process of being destroyed.
707	///
708	/// All members can now be permissionlessly unbonded, and the pool can never go back to any
709	/// other state other than being dissolved.
710	Destroying,
711}
712
713/// Pool administration roles.
714///
715/// Any pool has a depositor, which can never change. But, all the other roles are optional, and
716/// cannot exist. Note that if `root` is set to `None`, it basically means that the roles of this
717/// pool can never change again (except via governance).
718#[derive(
719	Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, TypeInfo, Debug, PartialEq, Clone,
720)]
721pub struct PoolRoles<AccountId> {
722	/// Creates the pool and is the initial member. They can only leave the pool once all other
723	/// members have left. Once they fully leave, the pool is destroyed.
724	pub depositor: AccountId,
725	/// Can change the nominator, bouncer, or itself and can perform any of the actions the
726	/// nominator or bouncer can.
727	pub root: Option<AccountId>,
728	/// Can select which validators the pool nominates.
729	pub nominator: Option<AccountId>,
730	/// Can change the pools state and kick members if the pool is blocked.
731	pub bouncer: Option<AccountId>,
732}
733
734// A pool's possible commission claiming permissions.
735#[derive(
736	PartialEq,
737	Eq,
738	Copy,
739	Clone,
740	Encode,
741	Decode,
742	DecodeWithMemTracking,
743	Debug,
744	TypeInfo,
745	MaxEncodedLen,
746)]
747pub enum CommissionClaimPermission<AccountId> {
748	Permissionless,
749	Account(AccountId),
750}
751
752/// Pool commission.
753///
754/// The pool `root` can set commission configuration after pool creation. By default, all commission
755/// values are `None`. Pool `root` can also set `max` and `change_rate` configurations before
756/// setting an initial `current` commission.
757///
758/// `current` is a tuple of the commission percentage and payee of commission. `throttle_from`
759/// keeps track of which block `current` was last updated. A `max` commission value can only be
760/// decreased after the initial value is set, to prevent commission from repeatedly increasing.
761///
762/// An optional commission `change_rate` allows the pool to set strict limits to how much commission
763/// can change in each update, and how often updates can take place.
764#[derive(
765	Encode,
766	Decode,
767	DecodeWithMemTracking,
768	DefaultNoBound,
769	MaxEncodedLen,
770	TypeInfo,
771	DebugNoBound,
772	PartialEq,
773	Copy,
774	Clone,
775)]
776#[codec(mel_bound(T: Config))]
777#[scale_info(skip_type_params(T))]
778pub struct Commission<T: Config> {
779	/// Optional commission rate of the pool along with the account commission is paid to.
780	pub current: Option<(Perbill, T::AccountId)>,
781	/// Optional maximum commission that can be set by the pool `root`. Once set, this value can
782	/// only be updated to a decreased value.
783	pub max: Option<Perbill>,
784	/// Optional configuration around how often commission can be updated, and when the last
785	/// commission update took place.
786	pub change_rate: Option<CommissionChangeRate<BlockNumberFor<T>>>,
787	/// The block from where throttling should be checked from. This value will be updated on all
788	/// commission updates and when setting an initial `change_rate`.
789	pub throttle_from: Option<BlockNumberFor<T>>,
790	// Whether commission can be claimed permissionlessly, or whether an account can claim
791	// commission. `Root` role can always claim.
792	pub claim_permission: Option<CommissionClaimPermission<T::AccountId>>,
793}
794
795impl<T: Config> Commission<T> {
796	/// Returns true if the current commission updating to `to` would exhaust the change rate
797	/// limits.
798	///
799	/// A commission update will be throttled (disallowed) if:
800	/// 1. not enough blocks have passed since the `throttle_from` block, if exists, or
801	/// 2. the new commission is greater than the maximum allowed increase.
802	fn throttling(&self, to: &Perbill) -> bool {
803		if let Some(t) = self.change_rate.as_ref() {
804			let commission_as_percent =
805				self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero());
806
807			// do not throttle if `to` is the same or a decrease in commission.
808			if *to <= commission_as_percent {
809				return false;
810			}
811			// Test for `max_increase` throttling.
812			//
813			// Throttled if the attempted increase in commission is greater than `max_increase`.
814			if (*to).saturating_sub(commission_as_percent) > t.max_increase {
815				return true;
816			}
817
818			// Test for `min_delay` throttling.
819			//
820			// Note: matching `None` is defensive only. `throttle_from` should always exist where
821			// `change_rate` has already been set, so this scenario should never happen.
822			return self.throttle_from.map_or_else(
823				|| {
824					defensive!("throttle_from should exist if change_rate is set");
825					true
826				},
827				|f| {
828					// if `min_delay` is zero (no delay), not throttling.
829					if t.min_delay == Zero::zero() {
830						false
831					} else {
832						// throttling if blocks passed is less than `min_delay`.
833						let blocks_surpassed =
834							T::BlockNumberProvider::current_block_number().saturating_sub(f);
835						blocks_surpassed < t.min_delay
836					}
837				},
838			);
839		}
840		false
841	}
842
843	/// Gets the pool's current commission, or returns Perbill::zero if none is set.
844	/// Bounded to global max if current is greater than `GlobalMaxCommission`.
845	fn current(&self) -> Perbill {
846		self.current
847			.as_ref()
848			.map_or(Perbill::zero(), |(c, _)| *c)
849			.min(GlobalMaxCommission::<T>::get().unwrap_or(Bounded::max_value()))
850	}
851
852	/// Set the pool's commission.
853	///
854	/// Update commission based on `current`. If a `None` is supplied, allow the commission to be
855	/// removed without any change rate restrictions. Updates `throttle_from` to the current block.
856	/// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored.
857	fn try_update_current(&mut self, current: &Option<(Perbill, T::AccountId)>) -> DispatchResult {
858		self.current = match current {
859			None => None,
860			Some((commission, payee)) => {
861				ensure!(!self.throttling(commission), Error::<T>::CommissionChangeThrottled);
862				ensure!(
863					commission <= &GlobalMaxCommission::<T>::get().unwrap_or(Bounded::max_value()),
864					Error::<T>::CommissionExceedsGlobalMaximum
865				);
866				ensure!(
867					self.max.map_or(true, |m| commission <= &m),
868					Error::<T>::CommissionExceedsMaximum
869				);
870				if commission.is_zero() {
871					None
872				} else {
873					Some((*commission, payee.clone()))
874				}
875			},
876		};
877		self.register_update();
878		Ok(())
879	}
880
881	/// Set the pool's maximum commission.
882	///
883	/// The pool's maximum commission can initially be set to any value, and only smaller values
884	/// thereafter. If larger values are attempted, this function will return a dispatch error.
885	///
886	/// If `current.0` is larger than the updated max commission value, `current.0` will also be
887	/// updated to the new maximum. This will also register a `throttle_from` update.
888	/// A `PoolCommissionUpdated` event is triggered if `current.0` is updated.
889	fn try_update_max(&mut self, pool_id: PoolId, new_max: Perbill) -> DispatchResult {
890		ensure!(
891			new_max <= GlobalMaxCommission::<T>::get().unwrap_or(Bounded::max_value()),
892			Error::<T>::CommissionExceedsGlobalMaximum
893		);
894		if let Some(old) = self.max.as_mut() {
895			if new_max > *old {
896				return Err(Error::<T>::MaxCommissionRestricted.into());
897			}
898			*old = new_max;
899		} else {
900			self.max = Some(new_max)
901		};
902		let updated_current = self
903			.current
904			.as_mut()
905			.map(|(c, _)| {
906				let u = *c > new_max;
907				*c = (*c).min(new_max);
908				u
909			})
910			.unwrap_or(false);
911
912		if updated_current {
913			if let Some((_, payee)) = self.current.as_ref() {
914				Pallet::<T>::deposit_event(Event::<T>::PoolCommissionUpdated {
915					pool_id,
916					current: Some((new_max, payee.clone())),
917				});
918			}
919			self.register_update();
920		}
921		Ok(())
922	}
923
924	/// Set the pool's commission `change_rate`.
925	///
926	/// Once a change rate configuration has been set, only more restrictive values can be set
927	/// thereafter. These restrictions translate to increased `min_delay` values and decreased
928	/// `max_increase` values.
929	///
930	/// Update `throttle_from` to the current block upon setting change rate for the first time, so
931	/// throttling can be checked from this block.
932	fn try_update_change_rate(
933		&mut self,
934		change_rate: CommissionChangeRate<BlockNumberFor<T>>,
935	) -> DispatchResult {
936		ensure!(!&self.less_restrictive(&change_rate), Error::<T>::CommissionChangeRateNotAllowed);
937
938		if self.change_rate.is_none() {
939			self.register_update();
940		}
941		self.change_rate = Some(change_rate);
942		Ok(())
943	}
944
945	/// Updates a commission's `throttle_from` field to the current block.
946	fn register_update(&mut self) {
947		self.throttle_from = Some(T::BlockNumberProvider::current_block_number());
948	}
949
950	/// Checks whether a change rate is less restrictive than the current change rate, if any.
951	///
952	/// No change rate will always be less restrictive than some change rate, so where no
953	/// `change_rate` is currently set, `false` is returned.
954	fn less_restrictive(&self, new: &CommissionChangeRate<BlockNumberFor<T>>) -> bool {
955		self.change_rate
956			.as_ref()
957			.map(|c| new.max_increase > c.max_increase || new.min_delay < c.min_delay)
958			.unwrap_or(false)
959	}
960}
961
962/// Pool commission change rate preferences.
963///
964/// The pool root is able to set a commission change rate for their pool. A commission change rate
965/// consists of 2 values; (1) the maximum allowed commission change, and (2) the minimum amount of
966/// blocks that must elapse before commission updates are allowed again.
967///
968/// Commission change rates are not applied to decreases in commission.
969#[derive(
970	Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone,
971)]
972pub struct CommissionChangeRate<BlockNumber> {
973	/// The maximum amount the commission can be updated by per `min_delay` period.
974	pub max_increase: Perbill,
975	/// How often an update can take place.
976	pub min_delay: BlockNumber,
977}
978
979/// Pool permissions and state
980#[derive(
981	Encode, Decode, DecodeWithMemTracking, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone,
982)]
983#[codec(mel_bound(T: Config))]
984#[scale_info(skip_type_params(T))]
985pub struct BondedPoolInner<T: Config> {
986	/// The commission rate of the pool.
987	pub commission: Commission<T>,
988	/// Count of members that belong to the pool.
989	pub member_counter: u32,
990	/// Total points of all the members in the pool who are actively bonded.
991	pub points: BalanceOf<T>,
992	/// See [`PoolRoles`].
993	pub roles: PoolRoles<T::AccountId>,
994	/// The current state of the pool.
995	pub state: PoolState,
996}
997
998/// A wrapper for bonded pools, with utility functions.
999///
1000/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account
1001/// + id of the pool, for easier access.
1002#[derive(DebugNoBound)]
1003#[cfg_attr(feature = "std", derive(Clone, PartialEq))]
1004pub struct BondedPool<T: Config> {
1005	/// The identifier of the pool.
1006	id: PoolId,
1007	/// The inner fields.
1008	inner: BondedPoolInner<T>,
1009}
1010
1011impl<T: Config> core::ops::Deref for BondedPool<T> {
1012	type Target = BondedPoolInner<T>;
1013	fn deref(&self) -> &Self::Target {
1014		&self.inner
1015	}
1016}
1017
1018impl<T: Config> core::ops::DerefMut for BondedPool<T> {
1019	fn deref_mut(&mut self) -> &mut Self::Target {
1020		&mut self.inner
1021	}
1022}
1023
1024impl<T: Config> BondedPool<T> {
1025	/// Create a new bonded pool with the given roles and identifier.
1026	fn new(id: PoolId, roles: PoolRoles<T::AccountId>) -> Self {
1027		Self {
1028			id,
1029			inner: BondedPoolInner {
1030				commission: Commission::default(),
1031				member_counter: Zero::zero(),
1032				points: Zero::zero(),
1033				roles,
1034				state: PoolState::Open,
1035			},
1036		}
1037	}
1038
1039	/// Get [`Self`] from storage. Returns `None` if no entry for `pool_account` exists.
1040	pub fn get(id: PoolId) -> Option<Self> {
1041		BondedPools::<T>::try_get(id).ok().map(|inner| Self { id, inner })
1042	}
1043
1044	/// Get the bonded account id of this pool.
1045	fn bonded_account(&self) -> T::AccountId {
1046		Pallet::<T>::generate_bonded_account(self.id)
1047	}
1048
1049	/// Get the reward account id of this pool.
1050	fn reward_account(&self) -> T::AccountId {
1051		Pallet::<T>::generate_reward_account(self.id)
1052	}
1053
1054	/// Consume self and put into storage.
1055	fn put(self) {
1056		BondedPools::<T>::insert(self.id, self.inner);
1057	}
1058
1059	/// Consume self and remove from storage.
1060	fn remove(self) {
1061		BondedPools::<T>::remove(self.id);
1062	}
1063
1064	/// Convert the given amount of balance to points given the current pool state.
1065	///
1066	/// This is often used for bonding and issuing new funds into the pool.
1067	fn balance_to_point(&self, new_funds: BalanceOf<T>) -> BalanceOf<T> {
1068		let bonded_balance = T::StakeAdapter::active_stake(Pool::from(self.bonded_account()));
1069		Pallet::<T>::balance_to_point(bonded_balance, self.points, new_funds)
1070	}
1071
1072	/// Convert the given number of points to balance given the current pool state.
1073	///
1074	/// This is often used for unbonding.
1075	fn points_to_balance(&self, points: BalanceOf<T>) -> BalanceOf<T> {
1076		let bonded_balance = T::StakeAdapter::active_stake(Pool::from(self.bonded_account()));
1077		Pallet::<T>::point_to_balance(bonded_balance, self.points, points)
1078	}
1079
1080	/// Issue points to [`Self`] for `new_funds`.
1081	fn issue(&mut self, new_funds: BalanceOf<T>) -> BalanceOf<T> {
1082		let points_to_issue = self.balance_to_point(new_funds);
1083		self.points = self.points.saturating_add(points_to_issue);
1084		points_to_issue
1085	}
1086
1087	/// Dissolve some points from the pool i.e. unbond the given amount of points from this pool.
1088	/// This is the opposite of issuing some funds into the pool.
1089	///
1090	/// Mutates self in place, but does not write anything to storage.
1091	///
1092	/// Returns the equivalent balance amount that actually needs to get unbonded.
1093	fn dissolve(&mut self, points: BalanceOf<T>) -> BalanceOf<T> {
1094		// NOTE: do not optimize by removing `balance`. it must be computed before mutating
1095		// `self.point`.
1096		let balance = self.points_to_balance(points);
1097		self.points = self.points.saturating_sub(points);
1098		balance
1099	}
1100
1101	/// Increment the member counter. Ensures that the pool and system member limits are
1102	/// respected.
1103	fn try_inc_members(&mut self) -> Result<(), DispatchError> {
1104		ensure!(
1105			MaxPoolMembersPerPool::<T>::get()
1106				.map_or(true, |max_per_pool| self.member_counter < max_per_pool),
1107			Error::<T>::MaxPoolMembers
1108		);
1109		ensure!(
1110			MaxPoolMembers::<T>::get().map_or(true, |max| PoolMembers::<T>::count() < max),
1111			Error::<T>::MaxPoolMembers
1112		);
1113		self.member_counter = self.member_counter.checked_add(1).ok_or(Error::<T>::OverflowRisk)?;
1114		Ok(())
1115	}
1116
1117	/// Decrement the member counter.
1118	fn dec_members(mut self) -> Self {
1119		self.member_counter = self.member_counter.defensive_saturating_sub(1);
1120		self
1121	}
1122
1123	fn is_root(&self, who: &T::AccountId) -> bool {
1124		self.roles.root.as_ref().map_or(false, |root| root == who)
1125	}
1126
1127	fn is_bouncer(&self, who: &T::AccountId) -> bool {
1128		self.roles.bouncer.as_ref().map_or(false, |bouncer| bouncer == who)
1129	}
1130
1131	fn can_update_roles(&self, who: &T::AccountId) -> bool {
1132		self.is_root(who)
1133	}
1134
1135	fn can_nominate(&self, who: &T::AccountId) -> bool {
1136		self.is_root(who) ||
1137			self.roles.nominator.as_ref().map_or(false, |nominator| nominator == who)
1138	}
1139
1140	fn can_kick(&self, who: &T::AccountId) -> bool {
1141		self.state == PoolState::Blocked && (self.is_root(who) || self.is_bouncer(who))
1142	}
1143
1144	fn can_toggle_state(&self, who: &T::AccountId) -> bool {
1145		(self.is_root(who) || self.is_bouncer(who)) && !self.is_destroying()
1146	}
1147
1148	fn can_set_metadata(&self, who: &T::AccountId) -> bool {
1149		self.is_root(who) || self.is_bouncer(who)
1150	}
1151
1152	fn can_manage_commission(&self, who: &T::AccountId) -> bool {
1153		self.is_root(who)
1154	}
1155
1156	fn can_claim_commission(&self, who: &T::AccountId) -> bool {
1157		if let Some(permission) = self.commission.claim_permission.as_ref() {
1158			match permission {
1159				CommissionClaimPermission::Permissionless => true,
1160				CommissionClaimPermission::Account(account) => account == who || self.is_root(who),
1161			}
1162		} else {
1163			self.is_root(who)
1164		}
1165	}
1166
1167	fn is_destroying(&self) -> bool {
1168		matches!(self.state, PoolState::Destroying)
1169	}
1170
1171	fn is_destroying_and_only_depositor(&self, alleged_depositor_points: BalanceOf<T>) -> bool {
1172		// we need to ensure that `self.member_counter == 1` as well, because the depositor's
1173		// initial `MinCreateBond` (or more) is what guarantees that the ledger of the pool does not
1174		// get killed in the staking system, and that it does not fall below `MinimumNominatorBond`,
1175		// which could prevent other non-depositor members from fully leaving. Thus, all members
1176		// must withdraw, then depositor can unbond, and finally withdraw after waiting another
1177		// cycle.
1178		self.is_destroying() && self.points == alleged_depositor_points && self.member_counter == 1
1179	}
1180
1181	/// Whether or not the pool is ok to be in `PoolSate::Open`. If this returns an `Err`, then the
1182	/// pool is unrecoverable and should be in the destroying state.
1183	fn ok_to_be_open(&self) -> Result<(), DispatchError> {
1184		ensure!(!self.is_destroying(), Error::<T>::CanNotChangeState);
1185
1186		let bonded_balance = T::StakeAdapter::active_stake(Pool::from(self.bonded_account()));
1187		ensure!(!bonded_balance.is_zero(), Error::<T>::OverflowRisk);
1188
1189		let points_to_balance_ratio_floor = self
1190			.points
1191			// We checked for zero above
1192			.div(bonded_balance);
1193
1194		let max_points_to_balance = T::MaxPointsToBalance::get();
1195
1196		// Pool points can inflate relative to balance, but only if the pool is slashed.
1197		// If we cap the ratio of points:balance so one cannot join a pool that has been slashed
1198		// by `max_points_to_balance`%, if not zero.
1199		ensure!(
1200			points_to_balance_ratio_floor < max_points_to_balance.into(),
1201			Error::<T>::OverflowRisk
1202		);
1203
1204		// then we can be decently confident the bonding pool points will not overflow
1205		// `BalanceOf<T>`. Note that these are just heuristics.
1206
1207		Ok(())
1208	}
1209
1210	/// Check that the pool can accept a member with `new_funds`.
1211	fn ok_to_join(&self) -> Result<(), DispatchError> {
1212		ensure!(self.state == PoolState::Open, Error::<T>::NotOpen);
1213		self.ok_to_be_open()?;
1214		Ok(())
1215	}
1216
1217	fn ok_to_unbond_with(
1218		&self,
1219		caller: &T::AccountId,
1220		target_account: &T::AccountId,
1221		target_member: &PoolMember<T>,
1222		unbonding_points: BalanceOf<T>,
1223	) -> Result<(), DispatchError> {
1224		let is_permissioned = caller == target_account;
1225		let is_depositor = *target_account == self.roles.depositor;
1226		let is_full_unbond = unbonding_points == target_member.active_points();
1227
1228		let balance_after_unbond = {
1229			let new_depositor_points =
1230				target_member.active_points().saturating_sub(unbonding_points);
1231			let mut target_member_after_unbond = (*target_member).clone();
1232			target_member_after_unbond.points = new_depositor_points;
1233			target_member_after_unbond.active_balance()
1234		};
1235
1236		// any partial unbonding is only ever allowed if this unbond is permissioned.
1237		ensure!(
1238			is_permissioned || is_full_unbond,
1239			Error::<T>::PartialUnbondNotAllowedPermissionlessly
1240		);
1241
1242		// any unbond must comply with the balance condition:
1243		ensure!(
1244			is_full_unbond ||
1245				balance_after_unbond >=
1246					if is_depositor {
1247						Pallet::<T>::depositor_min_bond()
1248					} else {
1249						MinJoinBond::<T>::get()
1250					},
1251			Error::<T>::MinimumBondNotMet
1252		);
1253
1254		// additional checks:
1255		match (is_permissioned, is_depositor) {
1256			(true, false) => (),
1257			(true, true) => {
1258				// permission depositor unbond: if destroying and pool is empty, always allowed,
1259				// with no additional limits.
1260				if self.is_destroying_and_only_depositor(target_member.active_points()) {
1261					// everything good, let them unbond anything.
1262				} else {
1263					// depositor cannot fully unbond yet.
1264					ensure!(!is_full_unbond, Error::<T>::MinimumBondNotMet);
1265				}
1266			},
1267			(false, false) => {
1268				// If the pool is blocked, then an admin with kicking permissions can remove a
1269				// member. If the pool is being destroyed, anyone can remove a member
1270				debug_assert!(is_full_unbond);
1271				ensure!(
1272					self.can_kick(caller) || self.is_destroying(),
1273					Error::<T>::NotKickerOrDestroying
1274				)
1275			},
1276			(false, true) => {
1277				// Permissionless depositor unbond is only allowed for a full unbond, and only when
1278				// destroying with the depositor as sole remaining member. `is_full_unbond` is
1279				// already guaranteed by the outer `ensure!` above.
1280				debug_assert!(is_full_unbond);
1281				ensure!(
1282					self.is_destroying_and_only_depositor(target_member.active_points()),
1283					Error::<T>::DoesNotHavePermission
1284				);
1285			},
1286		};
1287
1288		Ok(())
1289	}
1290
1291	/// # Returns
1292	///
1293	/// * Ok(()) if [`Call::withdraw_unbonded`] can be called, `Err(DispatchError)` otherwise.
1294	fn ok_to_withdraw_unbonded_with(
1295		&self,
1296		caller: &T::AccountId,
1297		target_account: &T::AccountId,
1298	) -> Result<(), DispatchError> {
1299		// This isn't a depositor
1300		let is_permissioned = caller == target_account;
1301		ensure!(
1302			is_permissioned || self.can_kick(caller) || self.is_destroying(),
1303			Error::<T>::NotKickerOrDestroying
1304		);
1305		Ok(())
1306	}
1307
1308	/// Bond exactly `amount` from `who`'s funds into this pool. Increases the [`TotalValueLocked`]
1309	/// by `amount`.
1310	///
1311	/// If the bond is [`BondType::Create`], [`Staking::bond`] is called, and `who` is allowed to be
1312	/// killed. Otherwise, [`Staking::bond_extra`] is called and `who` cannot be killed.
1313	///
1314	/// Returns `Ok(points_issues)`, `Err` otherwise.
1315	fn try_bond_funds(
1316		&mut self,
1317		who: &T::AccountId,
1318		amount: BalanceOf<T>,
1319		ty: BondType,
1320	) -> Result<BalanceOf<T>, DispatchError> {
1321		// We must calculate the points issued *before* we bond who's funds, else points:balance
1322		// ratio will be wrong.
1323		let points_issued = self.issue(amount);
1324
1325		T::StakeAdapter::pledge_bond(
1326			Member::from(who.clone()),
1327			Pool::from(self.bonded_account()),
1328			&self.reward_account(),
1329			amount,
1330			ty,
1331		)?;
1332		TotalValueLocked::<T>::mutate(|tvl| {
1333			tvl.saturating_accrue(amount);
1334		});
1335
1336		Ok(points_issued)
1337	}
1338
1339	// Set the state of `self`, and deposit an event if the state changed. State should never be set
1340	// directly in in order to ensure a state change event is always correctly deposited.
1341	fn set_state(&mut self, state: PoolState) {
1342		if self.state != state {
1343			self.state = state;
1344			Pallet::<T>::deposit_event(Event::<T>::StateChanged {
1345				pool_id: self.id,
1346				new_state: state,
1347			});
1348		};
1349	}
1350}
1351
1352/// A reward pool.
1353///
1354/// A reward pool is not so much a pool anymore, since it does not contain any shares or points.
1355/// Rather, simply to fit nicely next to bonded pool and unbonding pools in terms of terminology. In
1356/// reality, a reward pool is just a container for a few pool-dependent data related to the rewards.
1357#[derive(
1358	Encode,
1359	Decode,
1360	MaxEncodedLen,
1361	DecodeWithMemTracking,
1362	TypeInfo,
1363	CloneNoBound,
1364	PartialEqNoBound,
1365	EqNoBound,
1366	DebugNoBound,
1367)]
1368#[cfg_attr(feature = "std", derive(DefaultNoBound))]
1369#[codec(mel_bound(T: Config))]
1370#[scale_info(skip_type_params(T))]
1371pub struct RewardPool<T: Config> {
1372	/// The last recorded value of the reward counter.
1373	///
1374	/// This is updated ONLY when the points in the bonded pool change, which means `join`,
1375	/// `bond_extra` and `unbond`, all of which is done through `update_recorded`.
1376	pub last_recorded_reward_counter: T::RewardCounter,
1377	/// The last recorded total payouts of the reward pool.
1378	///
1379	/// Payouts is essentially income of the pool.
1380	///
1381	/// Update criteria is same as that of `last_recorded_reward_counter`.
1382	pub last_recorded_total_payouts: BalanceOf<T>,
1383	/// Total amount that this pool has paid out so far to the members.
1384	pub total_rewards_claimed: BalanceOf<T>,
1385	/// The amount of commission pending to be claimed.
1386	pub total_commission_pending: BalanceOf<T>,
1387	/// The amount of commission that has been claimed.
1388	pub total_commission_claimed: BalanceOf<T>,
1389}
1390
1391impl<T: Config> RewardPool<T> {
1392	/// Getter for [`RewardPool::last_recorded_reward_counter`].
1393	pub(crate) fn last_recorded_reward_counter(&self) -> T::RewardCounter {
1394		self.last_recorded_reward_counter
1395	}
1396
1397	/// Register some rewards that are claimed from the pool by the members.
1398	fn register_claimed_reward(&mut self, reward: BalanceOf<T>) {
1399		self.total_rewards_claimed = self.total_rewards_claimed.saturating_add(reward);
1400	}
1401
1402	/// Update the recorded values of the reward pool.
1403	///
1404	/// This function MUST be called whenever the points in the bonded pool change, AND whenever the
1405	/// the pools commission is updated. The reason for the former is that a change in pool points
1406	/// will alter the share of the reward balance among pool members, and the reason for the latter
1407	/// is that a change in commission will alter the share of the reward balance among the pool.
1408	fn update_records(
1409		&mut self,
1410		id: PoolId,
1411		bonded_points: BalanceOf<T>,
1412		commission: Perbill,
1413	) -> Result<(), Error<T>> {
1414		let balance = Self::current_balance(id);
1415
1416		let (current_reward_counter, new_pending_commission) =
1417			self.current_reward_counter(id, bonded_points, commission)?;
1418
1419		// Store the reward counter at the time of this update. This is used in subsequent calls to
1420		// `current_reward_counter`, whereby newly pending rewards (in points) are added to this
1421		// value.
1422		self.last_recorded_reward_counter = current_reward_counter;
1423
1424		// Add any new pending commission that has been calculated from `current_reward_counter` to
1425		// determine the total pending commission at the time of this update.
1426		self.total_commission_pending =
1427			self.total_commission_pending.saturating_add(new_pending_commission);
1428
1429		// Total payouts are essentially the entire historical balance of the reward pool, equating
1430		// to the current balance + the total rewards that have left the pool + the total commission
1431		// that has left the pool.
1432		let last_recorded_total_payouts = balance
1433			.checked_add(&self.total_rewards_claimed.saturating_add(self.total_commission_claimed))
1434			.ok_or(Error::<T>::OverflowRisk)?;
1435
1436		// Store the total payouts at the time of this update.
1437		//
1438		// An increase in ED could cause `last_recorded_total_payouts` to decrease but we should not
1439		// allow that to happen since an already paid out reward cannot decrease. The reward account
1440		// might go in deficit temporarily in this exceptional case but it will be corrected once
1441		// new rewards are added to the pool.
1442		self.last_recorded_total_payouts =
1443			self.last_recorded_total_payouts.max(last_recorded_total_payouts);
1444
1445		Ok(())
1446	}
1447
1448	/// Get the current reward counter, based on the given `bonded_points` being the state of the
1449	/// bonded pool at this time.
1450	fn current_reward_counter(
1451		&self,
1452		id: PoolId,
1453		bonded_points: BalanceOf<T>,
1454		commission: Perbill,
1455	) -> Result<(T::RewardCounter, BalanceOf<T>), Error<T>> {
1456		let balance = Self::current_balance(id);
1457
1458		// Calculate the current payout balance. The first 3 values of this calculation added
1459		// together represent what the balance would be if no payouts were made. The
1460		// `last_recorded_total_payouts` is then subtracted from this value to cancel out previously
1461		// recorded payouts, leaving only the remaining payouts that have not been claimed.
1462		let current_payout_balance = balance
1463			.saturating_add(self.total_rewards_claimed)
1464			.saturating_add(self.total_commission_claimed)
1465			.saturating_sub(self.last_recorded_total_payouts);
1466
1467		// Split the `current_payout_balance` into claimable rewards and claimable commission
1468		// according to the current commission rate.
1469		let new_pending_commission = commission * current_payout_balance;
1470		let new_pending_rewards = current_payout_balance.saturating_sub(new_pending_commission);
1471
1472		// * accuracy notes regarding the multiplication in `checked_from_rational`:
1473		// `current_payout_balance` is a subset of the total_issuance at the very worse.
1474		// `bonded_points` are similarly, in a non-slashed pool, have the same granularity as
1475		// balance, and are thus below within the range of total_issuance. In the worse case
1476		// scenario, for `saturating_from_rational`, we have:
1477		//
1478		// dot_total_issuance * 10^18 / `minJoinBond`
1479		//
1480		// assuming `MinJoinBond == ED`
1481		//
1482		// dot_total_issuance * 10^18 / 10^10 = dot_total_issuance * 10^8
1483		//
1484		// which, with the current numbers, is a miniscule fraction of the u128 capacity.
1485		//
1486		// Thus, adding two values of type reward counter should be safe for ages in a chain like
1487		// Polkadot. The important note here is that `reward_pool.last_recorded_reward_counter` only
1488		// ever accumulates, but its semantics imply that it is less than total_issuance, when
1489		// represented as `FixedU128`, which means it is less than `total_issuance * 10^18`.
1490		//
1491		// * accuracy notes regarding `checked_from_rational` collapsing to zero, meaning that no
1492		//   reward can be claimed:
1493		//
1494		// largest `bonded_points`, such that the reward counter is non-zero, with `FixedU128` will
1495		// be when the payout is being computed. This essentially means `payout/bonded_points` needs
1496		// to be more than 1/1^18. Thus, assuming that `bonded_points` will always be less than `10
1497		// * dot_total_issuance`, if the reward_counter is the smallest possible value, the value of
1498		//   the
1499		// reward being calculated is:
1500		//
1501		// x / 10^20 = 1/ 10^18
1502		//
1503		// x = 100
1504		//
1505		// which is basically 10^-8 DOTs. See `smallest_claimable_reward` for an example of this.
1506		let current_reward_counter =
1507			T::RewardCounter::checked_from_rational(new_pending_rewards, bonded_points)
1508				.and_then(|ref r| self.last_recorded_reward_counter.checked_add(r))
1509				.ok_or(Error::<T>::OverflowRisk)?;
1510
1511		Ok((current_reward_counter, new_pending_commission))
1512	}
1513
1514	/// Current free balance of the reward pool.
1515	///
1516	/// This is sum of all the rewards that are claimable by pool members.
1517	fn current_balance(id: PoolId) -> BalanceOf<T> {
1518		T::Currency::reducible_balance(
1519			&Pallet::<T>::generate_reward_account(id),
1520			Preservation::Expendable,
1521			Fortitude::Polite,
1522		)
1523	}
1524}
1525
1526/// An unbonding pool. This is always mapped with an era.
1527#[derive(
1528	Encode,
1529	Decode,
1530	MaxEncodedLen,
1531	DecodeWithMemTracking,
1532	TypeInfo,
1533	DefaultNoBound,
1534	DebugNoBound,
1535	CloneNoBound,
1536	PartialEqNoBound,
1537	EqNoBound,
1538)]
1539#[codec(mel_bound(T: Config))]
1540#[scale_info(skip_type_params(T))]
1541pub struct UnbondPool<T: Config> {
1542	/// The points in this pool.
1543	pub points: BalanceOf<T>,
1544	/// The funds in the pool.
1545	pub balance: BalanceOf<T>,
1546}
1547
1548impl<T: Config> UnbondPool<T> {
1549	fn balance_to_point(&self, new_funds: BalanceOf<T>) -> BalanceOf<T> {
1550		Pallet::<T>::balance_to_point(self.balance, self.points, new_funds)
1551	}
1552
1553	fn point_to_balance(&self, points: BalanceOf<T>) -> BalanceOf<T> {
1554		Pallet::<T>::point_to_balance(self.balance, self.points, points)
1555	}
1556
1557	/// Issue the equivalent points of `new_funds` into self.
1558	///
1559	/// Returns the actual amounts of points issued.
1560	fn issue(&mut self, new_funds: BalanceOf<T>) -> BalanceOf<T> {
1561		let new_points = self.balance_to_point(new_funds);
1562		self.points = self.points.saturating_add(new_points);
1563		self.balance = self.balance.saturating_add(new_funds);
1564		new_points
1565	}
1566
1567	/// Dissolve some points from the unbonding pool, reducing the balance of the pool
1568	/// proportionally. This is the opposite of `issue`.
1569	///
1570	/// Returns the actual amount of `Balance` that was removed from the pool.
1571	fn dissolve(&mut self, points: BalanceOf<T>) -> BalanceOf<T> {
1572		let balance_to_unbond = self.point_to_balance(points);
1573		self.points = self.points.saturating_sub(points);
1574		self.balance = self.balance.saturating_sub(balance_to_unbond);
1575
1576		balance_to_unbond
1577	}
1578}
1579
1580#[derive(
1581	Encode,
1582	Decode,
1583	MaxEncodedLen,
1584	DecodeWithMemTracking,
1585	TypeInfo,
1586	DefaultNoBound,
1587	DebugNoBound,
1588	CloneNoBound,
1589	PartialEqNoBound,
1590	EqNoBound,
1591)]
1592#[codec(mel_bound(T: Config))]
1593#[scale_info(skip_type_params(T))]
1594pub struct SubPools<T: Config> {
1595	/// A general, era agnostic pool of funds that have fully unbonded. The pools
1596	/// of `Self::with_era` will lazily be merged into into this pool if they are
1597	/// older than the effective post-unbonding window (see `SubPools::maybe_merge_pools`).
1598	pub no_era: UnbondPool<T>,
1599	/// Map of era in which a pool becomes unbonded in => unbond pools.
1600	pub with_era: BoundedBTreeMap<EraIndex, UnbondPool<T>, T::MaxUnbondingPools>,
1601}
1602
1603impl<T: Config> SubPools<T> {
1604	/// Merge the oldest `with_era` unbond pools into the `no_era` unbond pool.
1605	///
1606	/// This is often used whilst getting the sub-pool from storage, thus it consumes and returns
1607	/// `Self` for ergonomic purposes.
1608	fn maybe_merge_pools(mut self, active_era: EraIndex) -> Self {
1609		// Retain `with_era` pools for ~`MaxUnbondingPools` eras after unlock.
1610		// E.g., if window is 2 and active era is 10, retain pools 9..=10.
1611		let effective_post_unbonding_window =
1612			T::MaxUnbondingPools::get().saturating_sub(T::StakeAdapter::bonding_duration());
1613		if let Some(newest_era_to_remove) = active_era.checked_sub(effective_post_unbonding_window)
1614		{
1615			self.with_era.retain(|k, v| {
1616				if *k > newest_era_to_remove {
1617					// keep
1618					true
1619				} else {
1620					// merge into the no-era pool
1621					self.no_era.points = self.no_era.points.saturating_add(v.points);
1622					self.no_era.balance = self.no_era.balance.saturating_add(v.balance);
1623					false
1624				}
1625			});
1626		}
1627
1628		self
1629	}
1630
1631	/// The sum of all unbonding balance, regardless of whether they are actually unlocked or not.
1632	#[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))]
1633	fn sum_unbonding_balance(&self) -> BalanceOf<T> {
1634		self.no_era.balance.saturating_add(
1635			self.with_era
1636				.values()
1637				.fold(BalanceOf::<T>::zero(), |acc, pool| acc.saturating_add(pool.balance)),
1638		)
1639	}
1640}
1641
1642#[frame_support::pallet]
1643pub mod pallet {
1644	use super::*;
1645	use frame_support::traits::StorageVersion;
1646	use frame_system::pallet_prelude::{
1647		ensure_root, ensure_signed, BlockNumberFor as SystemBlockNumberFor, OriginFor,
1648	};
1649	use sp_runtime::Perbill;
1650
1651	/// The in-code storage version.
1652	const STORAGE_VERSION: StorageVersion = StorageVersion::new(8);
1653
1654	#[pallet::pallet]
1655	#[pallet::storage_version(STORAGE_VERSION)]
1656	pub struct Pallet<T>(_);
1657
1658	#[pallet::config]
1659	pub trait Config: frame_system::Config {
1660		/// The overarching event type.
1661		#[allow(deprecated)]
1662		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
1663
1664		/// Weight information for extrinsics in this pallet.
1665		type WeightInfo: weights::WeightInfo;
1666
1667		/// The currency type used for nomination pool.
1668		type Currency: Mutate<Self::AccountId>
1669			+ MutateFreeze<Self::AccountId, Id = Self::RuntimeFreezeReason>;
1670
1671		/// The overarching freeze reason.
1672		type RuntimeFreezeReason: From<FreezeReason>;
1673
1674		/// The type that is used for reward counter.
1675		///
1676		/// The arithmetic of the reward counter might saturate based on the size of the
1677		/// `Currency::Balance`. If this happens, operations fails. Nonetheless, this type should be
1678		/// chosen such that this failure almost never happens, as if it happens, the pool basically
1679		/// needs to be dismantled (or all pools migrated to a larger `RewardCounter` type, which is
1680		/// a PITA to do).
1681		///
1682		/// See the inline code docs of `Member::pending_rewards` and `RewardPool::update_recorded`
1683		/// for example analysis. A [`sp_runtime::FixedU128`] should be fine for chains with balance
1684		/// types similar to that of Polkadot and Kusama, in the absence of severe slashing (or
1685		/// prevented via a reasonable `MaxPointsToBalance`), for many many years to come.
1686		type RewardCounter: FixedPointNumber + MaxEncodedLen + TypeInfo + Default + codec::FullCodec;
1687
1688		/// The nomination pool's pallet id.
1689		#[pallet::constant]
1690		type PalletId: Get<frame_support::PalletId>;
1691
1692		/// The maximum pool points-to-balance ratio that an `open` pool can have.
1693		///
1694		/// This is important in the event slashing takes place and the pool's points-to-balance
1695		/// ratio becomes disproportional.
1696		///
1697		/// Moreover, this relates to the `RewardCounter` type as well, as the arithmetic operations
1698		/// are a function of number of points, and by setting this value to e.g. 10, you ensure
1699		/// that the total number of points in the system are at most 10 times the total_issuance of
1700		/// the chain, in the absolute worse case.
1701		///
1702		/// For a value of 10, the threshold would be a pool points-to-balance ratio of 10:1.
1703		/// Such a scenario would also be the equivalent of the pool being 90% slashed.
1704		#[pallet::constant]
1705		type MaxPointsToBalance: Get<u8>;
1706
1707		/// The maximum number of simultaneous unbonding chunks that can exist per member.
1708		#[pallet::constant]
1709		type MaxUnbonding: Get<u32>;
1710
1711		/// Infallible method for converting `Currency::Balance` to `U256`.
1712		type BalanceToU256: Convert<BalanceOf<Self>, U256>;
1713
1714		/// Infallible method for converting `U256` to `Currency::Balance`.
1715		type U256ToBalance: Convert<U256, BalanceOf<Self>>;
1716
1717		/// The interface for nominating.
1718		///
1719		/// Note: Switching to a new [`StakeStrategy`] might require a migration of the storage.
1720		type StakeAdapter: StakeStrategy<AccountId = Self::AccountId, Balance = BalanceOf<Self>>;
1721
1722		/// The maximum number of distinct era-keyed unbonding sub-pools ([`SubPools::with_era`])
1723		/// that may exist at once. This is also the basis for how long each sub-pool is kept on
1724		/// its own (correct) points-to-balance ratio: a sub-pool is merged into the era-agnostic
1725		/// [`SubPools::no_era`] pool only after `MaxUnbondingPools - bonding_duration` eras
1726		/// have passed since its unlock era (see `SubPools::maybe_merge_pools`). Once merged
1727		/// the ratio can become skewed due to some slashed ratio getting merged in at some point.
1728		#[pallet::constant]
1729		type MaxUnbondingPools: Get<u32>;
1730
1731		/// The maximum length, in bytes, that a pools metadata maybe.
1732		type MaxMetadataLen: Get<u32>;
1733
1734		/// The origin that can manage pool configurations.
1735		type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
1736
1737		/// Provider for the block number. Normally this is the `frame_system` pallet.
1738		type BlockNumberProvider: BlockNumberProvider;
1739
1740		/// Restrict some accounts from participating in a nomination pool.
1741		type Filter: Contains<Self::AccountId>;
1742	}
1743
1744	/// The sum of funds across all pools.
1745	///
1746	/// This might be lower but never higher than the sum of `total_balance` of all [`PoolMembers`]
1747	/// because calling `pool_withdraw_unbonded` might decrease the total stake of the pool's
1748	/// `bonded_account` without adjusting the pallet-internal `UnbondingPool`'s.
1749	#[pallet::storage]
1750	pub type TotalValueLocked<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
1751
1752	/// Minimum amount to bond to join a pool.
1753	#[pallet::storage]
1754	pub type MinJoinBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
1755
1756	/// Minimum bond required to create a pool.
1757	///
1758	/// This is the amount that the depositor must put as their initial stake in the pool, as an
1759	/// indication of "skin in the game".
1760	///
1761	/// This is the value that will always exist in the staking ledger of the pool bonded account
1762	/// while all other accounts leave.
1763	#[pallet::storage]
1764	pub type MinCreateBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
1765
1766	/// Maximum number of nomination pools that can exist. If `None`, then an unbounded number of
1767	/// pools can exist.
1768	#[pallet::storage]
1769	pub type MaxPools<T: Config> = StorageValue<_, u32, OptionQuery>;
1770
1771	/// Maximum number of members that can exist in the system. If `None`, then the count
1772	/// members are not bound on a system wide basis.
1773	#[pallet::storage]
1774	pub type MaxPoolMembers<T: Config> = StorageValue<_, u32, OptionQuery>;
1775
1776	/// Maximum number of members that may belong to pool. If `None`, then the count of
1777	/// members is not bound on a per pool basis.
1778	#[pallet::storage]
1779	pub type MaxPoolMembersPerPool<T: Config> = StorageValue<_, u32, OptionQuery>;
1780
1781	/// The maximum commission that can be charged by a pool. Used on commission payouts to bound
1782	/// pool commissions that are > `GlobalMaxCommission`, necessary if a future
1783	/// `GlobalMaxCommission` is lower than some current pool commissions.
1784	#[pallet::storage]
1785	pub type GlobalMaxCommission<T: Config> = StorageValue<_, Perbill, OptionQuery>;
1786
1787	/// Active members.
1788	///
1789	/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
1790	#[pallet::storage]
1791	pub type PoolMembers<T: Config> =
1792		CountedStorageMap<_, Twox64Concat, T::AccountId, PoolMember<T>>;
1793
1794	/// Storage for bonded pools.
1795	// To get or insert a pool see [`BondedPool::get`] and [`BondedPool::put`]
1796	#[pallet::storage]
1797	pub type BondedPools<T: Config> =
1798		CountedStorageMap<_, Twox64Concat, PoolId, BondedPoolInner<T>>;
1799
1800	/// Reward pools. This is where there rewards for each pool accumulate. When a members payout is
1801	/// claimed, the balance comes out of the reward pool. Keyed by the bonded pools account.
1802	#[pallet::storage]
1803	pub type RewardPools<T: Config> = CountedStorageMap<_, Twox64Concat, PoolId, RewardPool<T>>;
1804
1805	/// Groups of unbonding pools. Each group of unbonding pools belongs to a
1806	/// bonded pool, hence the name sub-pools. Keyed by the bonded pools account.
1807	#[pallet::storage]
1808	pub type SubPoolsStorage<T: Config> = CountedStorageMap<_, Twox64Concat, PoolId, SubPools<T>>;
1809
1810	/// Metadata for the pool.
1811	#[pallet::storage]
1812	pub type Metadata<T: Config> =
1813		CountedStorageMap<_, Twox64Concat, PoolId, BoundedVec<u8, T::MaxMetadataLen>, ValueQuery>;
1814
1815	/// Ever increasing number of all pools created so far.
1816	#[pallet::storage]
1817	pub type LastPoolId<T: Config> = StorageValue<_, u32, ValueQuery>;
1818
1819	/// A reverse lookup from the pool's account id to its id.
1820	///
1821	/// This is only used for slashing and on automatic withdraw update. In all other instances, the
1822	/// pool id is used, and the accounts are deterministically derived from it.
1823	#[pallet::storage]
1824	pub type ReversePoolIdLookup<T: Config> =
1825		CountedStorageMap<_, Twox64Concat, T::AccountId, PoolId, OptionQuery>;
1826
1827	/// Map from a pool member account to their opted claim permission.
1828	#[pallet::storage]
1829	pub type ClaimPermissions<T: Config> =
1830		StorageMap<_, Twox64Concat, T::AccountId, ClaimPermission, ValueQuery>;
1831
1832	#[pallet::genesis_config]
1833	pub struct GenesisConfig<T: Config> {
1834		pub min_join_bond: BalanceOf<T>,
1835		pub min_create_bond: BalanceOf<T>,
1836		pub max_pools: Option<u32>,
1837		pub max_members_per_pool: Option<u32>,
1838		pub max_members: Option<u32>,
1839		pub global_max_commission: Option<Perbill>,
1840	}
1841
1842	impl<T: Config> Default for GenesisConfig<T> {
1843		fn default() -> Self {
1844			Self {
1845				min_join_bond: Zero::zero(),
1846				min_create_bond: Zero::zero(),
1847				max_pools: Some(16),
1848				max_members_per_pool: Some(32),
1849				max_members: Some(16 * 32),
1850				global_max_commission: None,
1851			}
1852		}
1853	}
1854
1855	#[pallet::genesis_build]
1856	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
1857		fn build(&self) {
1858			MinJoinBond::<T>::put(self.min_join_bond);
1859			MinCreateBond::<T>::put(self.min_create_bond);
1860
1861			if let Some(max_pools) = self.max_pools {
1862				MaxPools::<T>::put(max_pools);
1863			}
1864			if let Some(max_members_per_pool) = self.max_members_per_pool {
1865				MaxPoolMembersPerPool::<T>::put(max_members_per_pool);
1866			}
1867			if let Some(max_members) = self.max_members {
1868				MaxPoolMembers::<T>::put(max_members);
1869			}
1870			if let Some(global_max_commission) = self.global_max_commission {
1871				GlobalMaxCommission::<T>::put(global_max_commission);
1872			}
1873		}
1874	}
1875
1876	/// Events of this pallet.
1877	#[pallet::event]
1878	#[pallet::generate_deposit(pub(crate) fn deposit_event)]
1879	pub enum Event<T: Config> {
1880		/// A pool has been created.
1881		Created { depositor: T::AccountId, pool_id: PoolId },
1882		/// A member has became bonded in a pool.
1883		Bonded { member: T::AccountId, pool_id: PoolId, bonded: BalanceOf<T>, joined: bool },
1884		/// A payout has been made to a member.
1885		PaidOut { member: T::AccountId, pool_id: PoolId, payout: BalanceOf<T> },
1886		/// A member has unbonded from their pool.
1887		///
1888		/// - `balance` is the corresponding balance of the number of points that has been
1889		///   requested to be unbonded (the argument of the `unbond` transaction) from the bonded
1890		///   pool.
1891		/// - `points` is the number of points that are issued as a result of `balance` being
1892		/// dissolved into the corresponding unbonding pool.
1893		/// - `era` is the era in which the balance will be unbonded.
1894		/// In the absence of slashing, these values will match. In the presence of slashing, the
1895		/// number of points that are issued in the unbonding pool will be less than the amount
1896		/// requested to be unbonded.
1897		Unbonded {
1898			member: T::AccountId,
1899			pool_id: PoolId,
1900			balance: BalanceOf<T>,
1901			points: BalanceOf<T>,
1902			era: EraIndex,
1903		},
1904		/// A member has withdrawn from their pool.
1905		///
1906		/// The given number of `points` have been dissolved in return of `balance`.
1907		///
1908		/// Similar to `Unbonded` event, in the absence of slashing, the ratio of point to balance
1909		/// will be 1.
1910		Withdrawn {
1911			member: T::AccountId,
1912			pool_id: PoolId,
1913			balance: BalanceOf<T>,
1914			points: BalanceOf<T>,
1915		},
1916		/// A pool has been destroyed.
1917		Destroyed { pool_id: PoolId },
1918		/// The state of a pool has changed
1919		StateChanged { pool_id: PoolId, new_state: PoolState },
1920		/// A member has been removed from a pool.
1921		///
1922		/// The removal can be voluntary (withdrawn all unbonded funds) or involuntary (kicked).
1923		/// Any funds that are still delegated (i.e. dangling delegation) are released and are
1924		/// represented by `released_balance`.
1925		MemberRemoved { pool_id: PoolId, member: T::AccountId, released_balance: BalanceOf<T> },
1926		/// The roles of a pool have been updated to the given new roles. Note that the depositor
1927		/// can never change.
1928		RolesUpdated {
1929			root: Option<T::AccountId>,
1930			bouncer: Option<T::AccountId>,
1931			nominator: Option<T::AccountId>,
1932		},
1933		/// The active balance of pool `pool_id` has been slashed to `balance`.
1934		PoolSlashed { pool_id: PoolId, balance: BalanceOf<T> },
1935		/// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`.
1936		UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf<T> },
1937		/// A pool's commission setting has been changed.
1938		PoolCommissionUpdated { pool_id: PoolId, current: Option<(Perbill, T::AccountId)> },
1939		/// A pool's maximum commission setting has been changed.
1940		PoolMaxCommissionUpdated { pool_id: PoolId, max_commission: Perbill },
1941		/// A pool's commission `change_rate` has been changed.
1942		PoolCommissionChangeRateUpdated {
1943			pool_id: PoolId,
1944			change_rate: CommissionChangeRate<BlockNumberFor<T>>,
1945		},
1946		/// Pool commission claim permission has been updated.
1947		PoolCommissionClaimPermissionUpdated {
1948			pool_id: PoolId,
1949			permission: Option<CommissionClaimPermission<T::AccountId>>,
1950		},
1951		/// Pool commission has been claimed.
1952		PoolCommissionClaimed { pool_id: PoolId, commission: BalanceOf<T> },
1953		/// Topped up deficit in frozen ED of the reward pool.
1954		MinBalanceDeficitAdjusted { pool_id: PoolId, amount: BalanceOf<T> },
1955		/// Claimed excess frozen ED of af the reward pool.
1956		MinBalanceExcessAdjusted { pool_id: PoolId, amount: BalanceOf<T> },
1957		/// A pool member's claim permission has been updated.
1958		MemberClaimPermissionUpdated { member: T::AccountId, permission: ClaimPermission },
1959		/// A pool's metadata was updated.
1960		MetadataUpdated { pool_id: PoolId, caller: T::AccountId },
1961		/// A pool's nominating account (or the pool's root account) has nominated a validator set
1962		/// on behalf of the pool.
1963		PoolNominationMade { pool_id: PoolId, caller: T::AccountId },
1964		/// The pool is chilled i.e. no longer nominating.
1965		PoolNominatorChilled { pool_id: PoolId, caller: T::AccountId },
1966		/// Global parameters regulating nomination pools have been updated.
1967		GlobalParamsUpdated {
1968			min_join_bond: BalanceOf<T>,
1969			min_create_bond: BalanceOf<T>,
1970			max_pools: Option<u32>,
1971			max_members: Option<u32>,
1972			max_members_per_pool: Option<u32>,
1973			global_max_commission: Option<Perbill>,
1974		},
1975	}
1976
1977	#[pallet::error]
1978	#[cfg_attr(test, derive(PartialEq))]
1979	pub enum Error<T> {
1980		/// A (bonded) pool id does not exist.
1981		PoolNotFound,
1982		/// An account is not a member.
1983		PoolMemberNotFound,
1984		/// A reward pool does not exist. In all cases this is a system logic error.
1985		RewardPoolNotFound,
1986		/// A sub pool does not exist.
1987		SubPoolsNotFound,
1988		/// An account is already delegating in another pool. An account may only belong to one
1989		/// pool at a time.
1990		AccountBelongsToOtherPool,
1991		/// The member is fully unbonded (and thus cannot access the bonded and reward pool
1992		/// anymore to, for example, collect rewards).
1993		FullyUnbonding,
1994		/// The member cannot unbond further chunks due to reaching the limit.
1995		MaxUnbondingLimit,
1996		/// None of the funds can be withdrawn yet because the bonding duration has not passed.
1997		CannotWithdrawAny,
1998		/// The amount does not meet the minimum bond to either join or create a pool.
1999		///
2000		/// The depositor can never unbond to a value less than `Pallet::depositor_min_bond`. The
2001		/// caller does not have nominating permissions for the pool. Members can never unbond to a
2002		/// value below `MinJoinBond`.
2003		MinimumBondNotMet,
2004		/// The transaction could not be executed due to overflow risk for the pool.
2005		OverflowRisk,
2006		/// A pool must be in [`PoolState::Destroying`] in order for the depositor to unbond or for
2007		/// other members to be permissionlessly unbonded.
2008		NotDestroying,
2009		/// The caller does not have nominating permissions for the pool.
2010		NotNominator,
2011		/// Either a) the caller cannot make a valid kick or b) the pool is not destroying.
2012		NotKickerOrDestroying,
2013		/// The pool is not open to join
2014		NotOpen,
2015		/// The system is maxed out on pools.
2016		MaxPools,
2017		/// Too many members in the pool or system.
2018		MaxPoolMembers,
2019		/// The pools state cannot be changed.
2020		CanNotChangeState,
2021		/// The caller does not have adequate permissions.
2022		DoesNotHavePermission,
2023		/// Metadata exceeds [`Config::MaxMetadataLen`]
2024		MetadataExceedsMaxLen,
2025		/// Some error occurred that should never happen. This should be reported to the
2026		/// maintainers.
2027		Defensive(DefensiveError),
2028		/// Partial unbonding now allowed permissionlessly.
2029		PartialUnbondNotAllowedPermissionlessly,
2030		/// The pool's max commission cannot be set higher than the existing value.
2031		MaxCommissionRestricted,
2032		/// The supplied commission exceeds the max allowed commission.
2033		CommissionExceedsMaximum,
2034		/// The supplied commission exceeds global maximum commission.
2035		CommissionExceedsGlobalMaximum,
2036		/// Not enough blocks have surpassed since the last commission update.
2037		CommissionChangeThrottled,
2038		/// The submitted changes to commission change rate are not allowed.
2039		CommissionChangeRateNotAllowed,
2040		/// There is no pending commission to claim.
2041		NoPendingCommission,
2042		/// No commission current has been set.
2043		NoCommissionCurrentSet,
2044		/// Pool id currently in use.
2045		PoolIdInUse,
2046		/// Pool id provided is not correct/usable.
2047		InvalidPoolId,
2048		/// Bonding extra is restricted to the exact pending reward amount.
2049		BondExtraRestricted,
2050		/// No imbalance in the ED deposit for the pool.
2051		NothingToAdjust,
2052		/// No slash pending that can be applied to the member.
2053		NothingToSlash,
2054		/// The slash amount is too low to be applied.
2055		SlashTooLow,
2056		/// The pool or member delegation has already migrated to delegate stake.
2057		AlreadyMigrated,
2058		/// The pool or member delegation has not migrated yet to delegate stake.
2059		NotMigrated,
2060		/// This call is not allowed in the current state of the pallet.
2061		NotSupported,
2062		/// Account is restricted from participation in pools. This may happen if the account is
2063		/// staking in another way already.
2064		Restricted,
2065	}
2066
2067	#[derive(Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, PalletError, Debug)]
2068	pub enum DefensiveError {
2069		/// There isn't enough space in the unbond pool.
2070		NotEnoughSpaceInUnbondPool,
2071		/// A (bonded) pool id does not exist.
2072		PoolNotFound,
2073		/// A reward pool does not exist. In all cases this is a system logic error.
2074		RewardPoolNotFound,
2075		/// A sub pool does not exist.
2076		SubPoolsNotFound,
2077		/// The bonded account should only be killed by the staking system when the depositor is
2078		/// withdrawing
2079		BondedStashKilledPrematurely,
2080		/// The delegation feature is unsupported.
2081		DelegationUnsupported,
2082		/// Unable to slash to the member of the pool.
2083		SlashNotApplied,
2084	}
2085
2086	impl<T> From<DefensiveError> for Error<T> {
2087		fn from(e: DefensiveError) -> Error<T> {
2088			Error::<T>::Defensive(e)
2089		}
2090	}
2091
2092	/// A reason for freezing funds.
2093	#[pallet::composite_enum]
2094	pub enum FreezeReason {
2095		/// Pool reward account is restricted from going below Existential Deposit.
2096		#[codec(index = 0)]
2097		PoolMinBalance,
2098	}
2099
2100	#[pallet::call]
2101	impl<T: Config> Pallet<T> {
2102		/// Stake funds with a pool. The amount to bond is delegated (or transferred based on
2103		/// [`adapter::StakeStrategyType`]) from the member to the pool account and immediately
2104		/// increases the pool's bond.
2105		///
2106		/// The method of transferring the amount to the pool account is determined by
2107		/// [`adapter::StakeStrategyType`]. If the pool is configured to use
2108		/// [`adapter::StakeStrategyType::Delegate`], the funds remain in the account of
2109		/// the `origin`, while the pool gains the right to use these funds for staking.
2110		///
2111		/// # Note
2112		///
2113		/// * An account can only be a member of a single pool.
2114		/// * An account cannot join the same pool multiple times.
2115		/// * This call will *not* dust the member account, so the member must have at least
2116		///   `existential deposit + amount` in their account.
2117		/// * Only a pool with [`PoolState::Open`] can be joined
2118		#[pallet::call_index(0)]
2119		#[pallet::weight(T::WeightInfo::join())]
2120		pub fn join(
2121			origin: OriginFor<T>,
2122			#[pallet::compact] amount: BalanceOf<T>,
2123			pool_id: PoolId,
2124		) -> DispatchResult {
2125			let who = ensure_signed(origin)?;
2126			// ensure pool is not in an un-migrated state.
2127			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
2128
2129			// ensure account is not restricted from joining the pool.
2130			ensure!(!T::Filter::contains(&who), Error::<T>::Restricted);
2131
2132			ensure!(amount >= MinJoinBond::<T>::get(), Error::<T>::MinimumBondNotMet);
2133			// If a member already exists that means they already belong to a pool
2134			ensure!(!PoolMembers::<T>::contains_key(&who), Error::<T>::AccountBelongsToOtherPool);
2135
2136			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
2137			bonded_pool.ok_to_join()?;
2138
2139			let mut reward_pool = RewardPools::<T>::get(pool_id)
2140				.defensive_ok_or::<Error<T>>(DefensiveError::RewardPoolNotFound.into())?;
2141			// IMPORTANT: reward pool records must be updated with the old points.
2142			reward_pool.update_records(
2143				pool_id,
2144				bonded_pool.points,
2145				bonded_pool.commission.current(),
2146			)?;
2147
2148			bonded_pool.try_inc_members()?;
2149			let points_issued = bonded_pool.try_bond_funds(&who, amount, BondType::Extra)?;
2150
2151			PoolMembers::insert(
2152				who.clone(),
2153				PoolMember::<T> {
2154					pool_id,
2155					points: points_issued,
2156					// we just updated `last_known_reward_counter` to the current one in
2157					// `update_recorded`.
2158					last_recorded_reward_counter: reward_pool.last_recorded_reward_counter(),
2159					unbonding_eras: Default::default(),
2160				},
2161			);
2162
2163			Self::deposit_event(Event::<T>::Bonded {
2164				member: who,
2165				pool_id,
2166				bonded: amount,
2167				joined: true,
2168			});
2169
2170			bonded_pool.put();
2171			RewardPools::<T>::insert(pool_id, reward_pool);
2172
2173			Ok(())
2174		}
2175
2176		/// Bond `extra` more funds from `origin` into the pool to which they already belong.
2177		///
2178		/// Additional funds can come from either the free balance of the account, of from the
2179		/// accumulated rewards, see [`BondExtra`].
2180		///
2181		/// Bonding extra funds implies an automatic payout of all pending rewards as well.
2182		/// See `bond_extra_other` to bond pending rewards of `other` members.
2183		// NOTE: this transaction is implemented with the sole purpose of readability and
2184		// correctness, not optimization. We read/write several storage items multiple times instead
2185		// of just once, in the spirit reusing code.
2186		#[pallet::call_index(1)]
2187		#[pallet::weight(
2188			T::WeightInfo::bond_extra_transfer()
2189			.max(T::WeightInfo::bond_extra_other())
2190		)]
2191		pub fn bond_extra(origin: OriginFor<T>, extra: BondExtra<BalanceOf<T>>) -> DispatchResult {
2192			let who = ensure_signed(origin)?;
2193
2194			// ensure who is not in an un-migrated state.
2195			ensure!(
2196				!Self::api_member_needs_delegate_migration(who.clone()),
2197				Error::<T>::NotMigrated
2198			);
2199
2200			Self::do_bond_extra(who.clone(), who, extra)
2201		}
2202
2203		/// A bonded member can use this to claim their payout based on the rewards that the pool
2204		/// has accumulated since their last claimed payout (OR since joining if this is their first
2205		/// time claiming rewards). The payout will be transferred to the member's account.
2206		///
2207		/// The member will earn rewards pro rata based on the members stake vs the sum of the
2208		/// members in the pools stake. Rewards do not "expire".
2209		///
2210		/// See `claim_payout_other` to claim rewards on behalf of some `other` pool member.
2211		#[pallet::call_index(2)]
2212		#[pallet::weight(T::WeightInfo::claim_payout())]
2213		pub fn claim_payout(origin: OriginFor<T>) -> DispatchResult {
2214			let signer = ensure_signed(origin)?;
2215			// ensure signer is not in an un-migrated state.
2216			ensure!(
2217				!Self::api_member_needs_delegate_migration(signer.clone()),
2218				Error::<T>::NotMigrated
2219			);
2220
2221			Self::do_claim_payout(signer.clone(), signer)
2222		}
2223
2224		/// Unbond up to `unbonding_points` of the `member_account`'s funds from the pool. It
2225		/// implicitly collects the rewards one last time, since not doing so would mean some
2226		/// rewards would be forfeited.
2227		///
2228		/// Under certain conditions, this call can be dispatched permissionlessly (i.e. by any
2229		/// account).
2230		///
2231		/// # Conditions for a permissionless dispatch.
2232		///
2233		/// * The pool is blocked and the caller is either the root or bouncer. This is refereed to
2234		///   as a kick.
2235		/// * The pool is destroying and the member is not the depositor.
2236		/// * The pool is destroying, the member is the depositor and no other members are in the
2237		///   pool.
2238		///
2239		/// ## Conditions for permissioned dispatch (i.e. the caller is also the
2240		/// `member_account`):
2241		///
2242		/// * The caller is not the depositor.
2243		/// * The caller is the depositor, the pool is destroying and no other members are in the
2244		///   pool.
2245		///
2246		/// # Note
2247		///
2248		/// If there are too many unlocking chunks to unbond with the pool account,
2249		/// [`Call::pool_withdraw_unbonded`] can be called to try and minimize unlocking chunks.
2250		/// The [`StakingInterface::unbond`] will implicitly call [`Call::pool_withdraw_unbonded`]
2251		/// to try to free chunks if necessary (ie. if unbound was called and no unlocking chunks
2252		/// are available). However, it may not be possible to release the current unlocking chunks,
2253		/// in which case, the result of this call will likely be the `NoMoreChunks` error from the
2254		/// staking system.
2255		#[pallet::call_index(3)]
2256		#[pallet::weight(T::WeightInfo::unbond())]
2257		pub fn unbond(
2258			origin: OriginFor<T>,
2259			member_account: AccountIdLookupOf<T>,
2260			#[pallet::compact] unbonding_points: BalanceOf<T>,
2261		) -> DispatchResult {
2262			let who = ensure_signed(origin)?;
2263			let member_account = T::Lookup::lookup(member_account)?;
2264			// ensure member is not in an un-migrated state.
2265			ensure!(
2266				!Self::api_member_needs_delegate_migration(member_account.clone()),
2267				Error::<T>::NotMigrated
2268			);
2269
2270			let (mut member, mut bonded_pool, mut reward_pool) =
2271				Self::get_member_with_pools(&member_account)?;
2272
2273			bonded_pool.ok_to_unbond_with(&who, &member_account, &member, unbonding_points)?;
2274
2275			// Claim the the payout prior to unbonding. Once the user is unbonding their points no
2276			// longer exist in the bonded pool and thus they can no longer claim their payouts. It
2277			// is not strictly necessary to claim the rewards, but we do it here for UX.
2278			reward_pool.update_records(
2279				bonded_pool.id,
2280				bonded_pool.points,
2281				bonded_pool.commission.current(),
2282			)?;
2283			Self::do_reward_payout(
2284				&member_account,
2285				&mut member,
2286				&mut bonded_pool,
2287				&mut reward_pool,
2288			)?;
2289
2290			let active_era = T::StakeAdapter::current_era();
2291			let unbond_era = T::StakeAdapter::bonding_duration().saturating_add(active_era);
2292
2293			// Unbond in the actual underlying nominator.
2294			let unbonding_balance = bonded_pool.dissolve(unbonding_points);
2295			T::StakeAdapter::unbond(Pool::from(bonded_pool.bonded_account()), unbonding_balance)?;
2296
2297			// Note that we lazily create the unbonding pools here if they don't already exist
2298			let mut sub_pools = SubPoolsStorage::<T>::get(member.pool_id)
2299				.unwrap_or_default()
2300				.maybe_merge_pools(active_era);
2301
2302			// Update the unbond pool associated with the current era with the unbonded funds. Note
2303			// that we lazily create the unbond pool if it does not yet exist.
2304			if !sub_pools.with_era.contains_key(&unbond_era) {
2305				sub_pools
2306					.with_era
2307					.try_insert(unbond_era, UnbondPool::default())
2308					// The above call to `maybe_merge_pools` should ensure there is
2309					// always enough space to insert.
2310					.defensive_map_err::<Error<T>, _>(|_| {
2311						DefensiveError::NotEnoughSpaceInUnbondPool.into()
2312					})?;
2313			}
2314
2315			let points_unbonded = sub_pools
2316				.with_era
2317				.get_mut(&unbond_era)
2318				// The above check ensures the pool exists.
2319				.defensive_ok_or::<Error<T>>(DefensiveError::PoolNotFound.into())?
2320				.issue(unbonding_balance);
2321
2322			// Try and unbond in the member map.
2323			member.try_unbond(unbonding_points, points_unbonded, unbond_era)?;
2324
2325			Self::deposit_event(Event::<T>::Unbonded {
2326				member: member_account.clone(),
2327				pool_id: member.pool_id,
2328				points: points_unbonded,
2329				balance: unbonding_balance,
2330				era: unbond_era,
2331			});
2332
2333			// Now that we know everything has worked write the items to storage.
2334			SubPoolsStorage::insert(member.pool_id, sub_pools);
2335			Self::put_member_with_pools(&member_account, member, bonded_pool, reward_pool);
2336			Ok(())
2337		}
2338
2339		/// Call `withdraw_unbonded` for the pools account. This call can be made by any account.
2340		///
2341		/// This is useful if there are too many unlocking chunks to call `unbond`, and some
2342		/// can be cleared by withdrawing. In the case there are too many unlocking chunks, the user
2343		/// would probably see an error like `NoMoreChunks` emitted from the staking system when
2344		/// they attempt to unbond.
2345		#[pallet::call_index(4)]
2346		#[pallet::weight(T::WeightInfo::pool_withdraw_unbonded(*num_slashing_spans))]
2347		pub fn pool_withdraw_unbonded(
2348			origin: OriginFor<T>,
2349			pool_id: PoolId,
2350			num_slashing_spans: u32,
2351		) -> DispatchResult {
2352			ensure_signed(origin)?;
2353			// ensure pool is not in an un-migrated state.
2354			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
2355
2356			let pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
2357
2358			// For now we only allow a pool to withdraw unbonded if its not destroying. If the pool
2359			// is destroying then `withdraw_unbonded` can be used.
2360			ensure!(pool.state != PoolState::Destroying, Error::<T>::NotDestroying);
2361			T::StakeAdapter::withdraw_unbonded(
2362				Pool::from(pool.bonded_account()),
2363				num_slashing_spans,
2364			)?;
2365
2366			Ok(())
2367		}
2368
2369		/// Withdraw unbonded funds from `member_account`. If no bonded funds can be unbonded, an
2370		/// error is returned.
2371		///
2372		/// Under certain conditions, this call can be dispatched permissionlessly (i.e. by any
2373		/// account).
2374		///
2375		/// # Conditions for a permissionless dispatch
2376		///
2377		/// * The pool is in destroy mode and the target is not the depositor.
2378		/// * The target is the depositor and they are the only member in the sub pools.
2379		/// * The pool is blocked and the caller is either the root or bouncer.
2380		///
2381		/// # Conditions for permissioned dispatch
2382		///
2383		/// * The caller is the target and they are not the depositor.
2384		///
2385		/// # Note
2386		///
2387		/// - If the target is the depositor, the pool will be destroyed.
2388		/// - If the pool has any pending slash, we also try to slash the member before letting them
2389		/// withdraw. This calculation adds some weight overhead and is only defensive. In reality,
2390		/// pool slashes must have been already applied via permissionless [`Call::apply_slash`].
2391		#[pallet::call_index(5)]
2392		#[pallet::weight(
2393			T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans)
2394		)]
2395		pub fn withdraw_unbonded(
2396			origin: OriginFor<T>,
2397			member_account: AccountIdLookupOf<T>,
2398			num_slashing_spans: u32,
2399		) -> DispatchResultWithPostInfo {
2400			let caller = ensure_signed(origin)?;
2401			let member_account = T::Lookup::lookup(member_account)?;
2402			// ensure member is not in an un-migrated state.
2403			ensure!(
2404				!Self::api_member_needs_delegate_migration(member_account.clone()),
2405				Error::<T>::NotMigrated
2406			);
2407
2408			let mut member =
2409				PoolMembers::<T>::get(&member_account).ok_or(Error::<T>::PoolMemberNotFound)?;
2410			let active_era = T::StakeAdapter::current_era();
2411
2412			let bonded_pool = BondedPool::<T>::get(member.pool_id)
2413				.defensive_ok_or::<Error<T>>(DefensiveError::PoolNotFound.into())?;
2414			let mut sub_pools =
2415				SubPoolsStorage::<T>::get(member.pool_id).ok_or(Error::<T>::SubPoolsNotFound)?;
2416
2417			let slash_weight =
2418				// apply slash if any before withdraw.
2419				match Self::do_apply_slash(&member_account, None, false) {
2420					Ok(_) => T::WeightInfo::apply_slash(),
2421					Err(e) => {
2422						let no_pending_slash: DispatchResult = Err(Error::<T>::NothingToSlash.into());
2423						// This is an expected error. We add appropriate fees and continue withdrawal.
2424						if Err(e) == no_pending_slash {
2425							T::WeightInfo::apply_slash_fail()
2426						} else {
2427							// defensive: if we can't apply slash for some reason, we abort.
2428							return Err(Error::<T>::Defensive(DefensiveError::SlashNotApplied).into());
2429						}
2430					}
2431
2432				};
2433
2434			bonded_pool.ok_to_withdraw_unbonded_with(&caller, &member_account)?;
2435			let pool_account = bonded_pool.bonded_account();
2436
2437			// NOTE: must do this after we have done the `ok_to_withdraw_unbonded_other_with` check.
2438			let withdrawn_points = member.withdraw_unlocked(active_era);
2439			ensure!(!withdrawn_points.is_empty(), Error::<T>::CannotWithdrawAny);
2440
2441			// Before calculating the `balance_to_unbond`, we call withdraw unbonded to ensure the
2442			// `transferable_balance` is correct.
2443			let stash_killed = T::StakeAdapter::withdraw_unbonded(
2444				Pool::from(bonded_pool.bonded_account()),
2445				num_slashing_spans,
2446			)?;
2447
2448			// defensive-only: the depositor puts enough funds into the stash so that it will only
2449			// be destroyed when they are leaving.
2450			ensure!(
2451				!stash_killed || caller == bonded_pool.roles.depositor,
2452				Error::<T>::Defensive(DefensiveError::BondedStashKilledPrematurely)
2453			);
2454
2455			if stash_killed {
2456				// Maybe an extra consumer left on the pool account, if so, remove it.
2457				if frame_system::Pallet::<T>::consumers(&pool_account) == 1 {
2458					frame_system::Pallet::<T>::dec_consumers(&pool_account);
2459				}
2460
2461				// Note: This is not pretty, but we have to do this because of a bug where old pool
2462				// accounts might have had an extra consumer increment. We know at this point no
2463				// other pallet should depend on pool account so safe to do this.
2464				// Refer to following issues:
2465				// - https://github.com/paritytech/polkadot-sdk/issues/4440
2466				// - https://github.com/paritytech/polkadot-sdk/issues/2037
2467			}
2468
2469			let mut sum_unlocked_points: BalanceOf<T> = Zero::zero();
2470			let balance_to_unbond = withdrawn_points
2471				.iter()
2472				.fold(BalanceOf::<T>::zero(), |accumulator, (era, unlocked_points)| {
2473					sum_unlocked_points = sum_unlocked_points.saturating_add(*unlocked_points);
2474					if let Some(era_pool) = sub_pools.with_era.get_mut(era) {
2475						let balance_to_unbond = era_pool.dissolve(*unlocked_points);
2476						if era_pool.points.is_zero() {
2477							sub_pools.with_era.remove(era);
2478						}
2479						accumulator.saturating_add(balance_to_unbond)
2480					} else {
2481						// A pool does not belong to this era, so it must have been merged to the
2482						// era-less pool.
2483						accumulator.saturating_add(sub_pools.no_era.dissolve(*unlocked_points))
2484					}
2485				})
2486				// A call to this transaction may cause the pool's stash to get dusted. If this
2487				// happens before the last member has withdrawn, then all subsequent withdraws will
2488				// be 0. However the unbond pools do no get updated to reflect this. In the
2489				// aforementioned scenario, this check ensures we don't try to withdraw funds that
2490				// don't exist. This check is also defensive in cases where the unbond pool does not
2491				// update its balance (e.g. a bug in the slashing hook.) We gracefully proceed in
2492				// order to ensure members can leave the pool and it can be destroyed.
2493				.min(T::StakeAdapter::transferable_balance(
2494					Pool::from(bonded_pool.bonded_account()),
2495					Member::from(member_account.clone()),
2496				));
2497
2498			// this can fail if the pool uses `DelegateStake` strategy and the member delegation
2499			// is not claimed yet. See `Call::migrate_delegation()`.
2500			T::StakeAdapter::member_withdraw(
2501				Member::from(member_account.clone()),
2502				Pool::from(bonded_pool.bonded_account()),
2503				balance_to_unbond,
2504				num_slashing_spans,
2505			)?;
2506
2507			Self::deposit_event(Event::<T>::Withdrawn {
2508				member: member_account.clone(),
2509				pool_id: member.pool_id,
2510				points: sum_unlocked_points,
2511				balance: balance_to_unbond,
2512			});
2513
2514			let post_info_weight = if member.total_points().is_zero() {
2515				// remove any `ClaimPermission` associated with the member.
2516				ClaimPermissions::<T>::remove(&member_account);
2517
2518				// member being reaped.
2519				PoolMembers::<T>::remove(&member_account);
2520
2521				// Ensure any dangling delegation is withdrawn.
2522				let dangling_withdrawal = match T::StakeAdapter::member_delegation_balance(
2523					Member::from(member_account.clone()),
2524				) {
2525					Some(dangling_delegation) => {
2526						T::StakeAdapter::member_withdraw(
2527							Member::from(member_account.clone()),
2528							Pool::from(bonded_pool.bonded_account()),
2529							dangling_delegation,
2530							num_slashing_spans,
2531						)?;
2532						dangling_delegation
2533					},
2534					None => Zero::zero(),
2535				};
2536
2537				Self::deposit_event(Event::<T>::MemberRemoved {
2538					pool_id: member.pool_id,
2539					member: member_account.clone(),
2540					released_balance: dangling_withdrawal,
2541				});
2542
2543				if member_account == bonded_pool.roles.depositor {
2544					Pallet::<T>::dissolve_pool(bonded_pool);
2545					Weight::default()
2546				} else {
2547					bonded_pool.dec_members().put();
2548					SubPoolsStorage::<T>::insert(member.pool_id, sub_pools);
2549					T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)
2550				}
2551			} else {
2552				// we certainly don't need to delete any pools, because no one is being removed.
2553				SubPoolsStorage::<T>::insert(member.pool_id, sub_pools);
2554				PoolMembers::<T>::insert(&member_account, member);
2555				T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)
2556			};
2557
2558			Ok(Some(post_info_weight.saturating_add(slash_weight)).into())
2559		}
2560
2561		/// Create a new delegation pool.
2562		///
2563		/// # Arguments
2564		///
2565		/// * `amount` - The amount of funds to delegate to the pool. This also acts of a sort of
2566		///   deposit since the pools creator cannot fully unbond funds until the pool is being
2567		///   destroyed.
2568		/// * `index` - A disambiguation index for creating the account. Likely only useful when
2569		///   creating multiple pools in the same extrinsic.
2570		/// * `root` - The account to set as [`PoolRoles::root`].
2571		/// * `nominator` - The account to set as the [`PoolRoles::nominator`].
2572		/// * `bouncer` - The account to set as the [`PoolRoles::bouncer`].
2573		///
2574		/// # Note
2575		///
2576		/// In addition to `amount`, the caller will transfer the existential deposit; so the caller
2577		/// needs at have at least `amount + existential_deposit` transferable.
2578		#[pallet::call_index(6)]
2579		#[pallet::weight(T::WeightInfo::create())]
2580		pub fn create(
2581			origin: OriginFor<T>,
2582			#[pallet::compact] amount: BalanceOf<T>,
2583			root: AccountIdLookupOf<T>,
2584			nominator: AccountIdLookupOf<T>,
2585			bouncer: AccountIdLookupOf<T>,
2586		) -> DispatchResult {
2587			let depositor = ensure_signed(origin)?;
2588
2589			let pool_id = LastPoolId::<T>::try_mutate::<_, Error<T>, _>(|id| {
2590				*id = id.checked_add(1).ok_or(Error::<T>::OverflowRisk)?;
2591				Ok(*id)
2592			})?;
2593
2594			Self::do_create(depositor, amount, root, nominator, bouncer, pool_id)
2595		}
2596
2597		/// Create a new delegation pool with a previously used pool id
2598		///
2599		/// # Arguments
2600		///
2601		/// same as `create` with the inclusion of
2602		/// * `pool_id` - `A valid PoolId.
2603		#[pallet::call_index(7)]
2604		#[pallet::weight(T::WeightInfo::create())]
2605		pub fn create_with_pool_id(
2606			origin: OriginFor<T>,
2607			#[pallet::compact] amount: BalanceOf<T>,
2608			root: AccountIdLookupOf<T>,
2609			nominator: AccountIdLookupOf<T>,
2610			bouncer: AccountIdLookupOf<T>,
2611			pool_id: PoolId,
2612		) -> DispatchResult {
2613			let depositor = ensure_signed(origin)?;
2614
2615			ensure!(!BondedPools::<T>::contains_key(pool_id), Error::<T>::PoolIdInUse);
2616			ensure!(pool_id < LastPoolId::<T>::get(), Error::<T>::InvalidPoolId);
2617
2618			Self::do_create(depositor, amount, root, nominator, bouncer, pool_id)
2619		}
2620
2621		/// Nominate on behalf of the pool.
2622		///
2623		/// The dispatch origin of this call must be signed by the pool nominator or the pool
2624		/// root role.
2625		///
2626		/// This directly forwards the call to an implementation of `StakingInterface` (e.g.,
2627		/// `pallet-staking`) through [`Config::StakeAdapter`], on behalf of the bonded pool.
2628		///
2629		/// # Note
2630		///
2631		/// In addition to a `root` or `nominator` role of `origin`, the pool's depositor needs to
2632		/// have at least `depositor_min_bond` in the pool to start nominating.
2633		#[pallet::call_index(8)]
2634		#[pallet::weight(T::WeightInfo::nominate(validators.len() as u32))]
2635		pub fn nominate(
2636			origin: OriginFor<T>,
2637			pool_id: PoolId,
2638			validators: Vec<T::AccountId>,
2639		) -> DispatchResult {
2640			let who = ensure_signed(origin)?;
2641			let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
2642			// ensure pool is not in an un-migrated state.
2643			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
2644			ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator);
2645
2646			let depositor_points = PoolMembers::<T>::get(&bonded_pool.roles.depositor)
2647				.ok_or(Error::<T>::PoolMemberNotFound)?
2648				.active_points();
2649
2650			ensure!(
2651				bonded_pool.points_to_balance(depositor_points) >= Self::depositor_min_bond(),
2652				Error::<T>::MinimumBondNotMet
2653			);
2654
2655			T::StakeAdapter::nominate(Pool::from(bonded_pool.bonded_account()), validators).map(
2656				|_| Self::deposit_event(Event::<T>::PoolNominationMade { pool_id, caller: who }),
2657			)
2658		}
2659
2660		/// Set a new state for the pool.
2661		///
2662		/// If a pool is already in the `Destroying` state, then under no condition can its state
2663		/// change again.
2664		///
2665		/// The dispatch origin of this call must be either:
2666		///
2667		/// 1. signed by the bouncer, or the root role of the pool,
2668		/// 2. if the pool conditions to be open are NOT met (as described by `ok_to_be_open`), and
2669		///    then the state of the pool can be permissionlessly changed to `Destroying`.
2670		#[pallet::call_index(9)]
2671		#[pallet::weight(T::WeightInfo::set_state())]
2672		pub fn set_state(
2673			origin: OriginFor<T>,
2674			pool_id: PoolId,
2675			state: PoolState,
2676		) -> DispatchResult {
2677			let who = ensure_signed(origin)?;
2678			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
2679			ensure!(bonded_pool.state != PoolState::Destroying, Error::<T>::CanNotChangeState);
2680			// ensure pool is not in an un-migrated state.
2681			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
2682
2683			if bonded_pool.can_toggle_state(&who) {
2684				bonded_pool.set_state(state);
2685			} else if bonded_pool.ok_to_be_open().is_err() && state == PoolState::Destroying {
2686				// If the pool has bad properties, then anyone can set it as destroying
2687				bonded_pool.set_state(PoolState::Destroying);
2688			} else {
2689				Err(Error::<T>::CanNotChangeState)?;
2690			}
2691
2692			bonded_pool.put();
2693
2694			Ok(())
2695		}
2696
2697		/// Set a new metadata for the pool.
2698		///
2699		/// The dispatch origin of this call must be signed by the bouncer, or the root role of the
2700		/// pool.
2701		#[pallet::call_index(10)]
2702		#[pallet::weight(T::WeightInfo::set_metadata(metadata.len() as u32))]
2703		pub fn set_metadata(
2704			origin: OriginFor<T>,
2705			pool_id: PoolId,
2706			metadata: Vec<u8>,
2707		) -> DispatchResult {
2708			let who = ensure_signed(origin)?;
2709			let metadata: BoundedVec<_, _> =
2710				metadata.try_into().map_err(|_| Error::<T>::MetadataExceedsMaxLen)?;
2711			ensure!(
2712				BondedPool::<T>::get(pool_id)
2713					.ok_or(Error::<T>::PoolNotFound)?
2714					.can_set_metadata(&who),
2715				Error::<T>::DoesNotHavePermission
2716			);
2717			// ensure pool is not in an un-migrated state.
2718			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
2719
2720			Metadata::<T>::mutate(pool_id, |pool_meta| *pool_meta = metadata);
2721
2722			Self::deposit_event(Event::<T>::MetadataUpdated { pool_id, caller: who });
2723
2724			Ok(())
2725		}
2726
2727		/// Update configurations for the nomination pools. The origin for this call must be
2728		/// [`Config::AdminOrigin`].
2729		///
2730		/// # Arguments
2731		///
2732		/// * `min_join_bond` - Set [`MinJoinBond`].
2733		/// * `min_create_bond` - Set [`MinCreateBond`].
2734		/// * `max_pools` - Set [`MaxPools`].
2735		/// * `max_members` - Set [`MaxPoolMembers`].
2736		/// * `max_members_per_pool` - Set [`MaxPoolMembersPerPool`].
2737		/// * `global_max_commission` - Set [`GlobalMaxCommission`].
2738		#[pallet::call_index(11)]
2739		#[pallet::weight(T::WeightInfo::set_configs())]
2740		pub fn set_configs(
2741			origin: OriginFor<T>,
2742			min_join_bond: ConfigOp<BalanceOf<T>>,
2743			min_create_bond: ConfigOp<BalanceOf<T>>,
2744			max_pools: ConfigOp<u32>,
2745			max_members: ConfigOp<u32>,
2746			max_members_per_pool: ConfigOp<u32>,
2747			global_max_commission: ConfigOp<Perbill>,
2748		) -> DispatchResult {
2749			T::AdminOrigin::ensure_origin(origin)?;
2750
2751			macro_rules! config_op_exp {
2752				($storage:ty, $op:ident) => {
2753					match $op {
2754						ConfigOp::Noop => (),
2755						ConfigOp::Set(v) => <$storage>::put(v),
2756						ConfigOp::Remove => <$storage>::kill(),
2757					}
2758				};
2759			}
2760
2761			config_op_exp!(MinJoinBond::<T>, min_join_bond);
2762			config_op_exp!(MinCreateBond::<T>, min_create_bond);
2763			config_op_exp!(MaxPools::<T>, max_pools);
2764			config_op_exp!(MaxPoolMembers::<T>, max_members);
2765			config_op_exp!(MaxPoolMembersPerPool::<T>, max_members_per_pool);
2766			config_op_exp!(GlobalMaxCommission::<T>, global_max_commission);
2767
2768			Self::deposit_event(Event::<T>::GlobalParamsUpdated {
2769				min_join_bond: MinJoinBond::<T>::get(),
2770				min_create_bond: MinCreateBond::<T>::get(),
2771				max_pools: MaxPools::<T>::get(),
2772				max_members: MaxPoolMembers::<T>::get(),
2773				max_members_per_pool: MaxPoolMembersPerPool::<T>::get(),
2774				global_max_commission: GlobalMaxCommission::<T>::get(),
2775			});
2776
2777			Ok(())
2778		}
2779
2780		/// Update the roles of the pool.
2781		///
2782		/// The root is the only entity that can change any of the roles, including itself,
2783		/// excluding the depositor, who can never change.
2784		///
2785		/// It emits an event, notifying UIs of the role change. This event is quite relevant to
2786		/// most pool members and they should be informed of changes to pool roles.
2787		#[pallet::call_index(12)]
2788		#[pallet::weight(T::WeightInfo::update_roles())]
2789		pub fn update_roles(
2790			origin: OriginFor<T>,
2791			pool_id: PoolId,
2792			new_root: ConfigOp<T::AccountId>,
2793			new_nominator: ConfigOp<T::AccountId>,
2794			new_bouncer: ConfigOp<T::AccountId>,
2795		) -> DispatchResult {
2796			let mut bonded_pool = match ensure_root(origin.clone()) {
2797				Ok(()) => BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?,
2798				Err(sp_runtime::traits::BadOrigin) => {
2799					let who = ensure_signed(origin)?;
2800					let bonded_pool =
2801						BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
2802					ensure!(bonded_pool.can_update_roles(&who), Error::<T>::DoesNotHavePermission);
2803					bonded_pool
2804				},
2805			};
2806
2807			// ensure pool is not in an un-migrated state.
2808			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
2809
2810			match new_root {
2811				ConfigOp::Noop => (),
2812				ConfigOp::Remove => bonded_pool.roles.root = None,
2813				ConfigOp::Set(v) => bonded_pool.roles.root = Some(v),
2814			};
2815			match new_nominator {
2816				ConfigOp::Noop => (),
2817				ConfigOp::Remove => bonded_pool.roles.nominator = None,
2818				ConfigOp::Set(v) => bonded_pool.roles.nominator = Some(v),
2819			};
2820			match new_bouncer {
2821				ConfigOp::Noop => (),
2822				ConfigOp::Remove => bonded_pool.roles.bouncer = None,
2823				ConfigOp::Set(v) => bonded_pool.roles.bouncer = Some(v),
2824			};
2825
2826			Self::deposit_event(Event::<T>::RolesUpdated {
2827				root: bonded_pool.roles.root.clone(),
2828				nominator: bonded_pool.roles.nominator.clone(),
2829				bouncer: bonded_pool.roles.bouncer.clone(),
2830			});
2831
2832			bonded_pool.put();
2833			Ok(())
2834		}
2835
2836		/// Chill on behalf of the pool.
2837		///
2838		/// The dispatch origin of this call can be signed by the pool nominator or the pool
2839		/// root role, same as [`Pallet::nominate`].
2840		///
2841		/// This directly forwards the call to an implementation of `StakingInterface` (e.g.,
2842		/// `pallet-staking`) through [`Config::StakeAdapter`], on behalf of the bonded pool.
2843		///
2844		/// Under certain conditions, this call can be dispatched permissionlessly (i.e. by any
2845		/// account).
2846		///
2847		/// # Conditions for a permissionless dispatch:
2848		/// * When pool depositor has less than `MinNominatorBond` staked, otherwise pool members
2849		///   are unable to unbond.
2850		///
2851		/// # Conditions for permissioned dispatch:
2852		/// * The caller is the pool's nominator or root.
2853		#[pallet::call_index(13)]
2854		#[pallet::weight(T::WeightInfo::chill())]
2855		pub fn chill(origin: OriginFor<T>, pool_id: PoolId) -> DispatchResult {
2856			let who = ensure_signed(origin)?;
2857			let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
2858			// ensure pool is not in an un-migrated state.
2859			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
2860
2861			let depositor_points = PoolMembers::<T>::get(&bonded_pool.roles.depositor)
2862				.ok_or(Error::<T>::PoolMemberNotFound)?
2863				.active_points();
2864
2865			if bonded_pool.points_to_balance(depositor_points) >=
2866				T::StakeAdapter::minimum_nominator_bond()
2867			{
2868				ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator);
2869			}
2870
2871			T::StakeAdapter::chill(Pool::from(bonded_pool.bonded_account())).map(|_| {
2872				Self::deposit_event(Event::<T>::PoolNominatorChilled { pool_id, caller: who })
2873			})
2874		}
2875
2876		/// `origin` bonds funds from `extra` for some pool member `member` into their respective
2877		/// pools.
2878		///
2879		/// `origin` can bond extra funds from free balance or pending rewards when `origin ==
2880		/// other`.
2881		///
2882		/// In the case of `origin != other`, `origin` can only bond extra pending rewards of
2883		/// `other` members assuming set_claim_permission for the given member is
2884		/// `PermissionlessCompound` or `PermissionlessAll`.
2885		#[pallet::call_index(14)]
2886		#[pallet::weight(
2887			T::WeightInfo::bond_extra_transfer()
2888			.max(T::WeightInfo::bond_extra_other())
2889		)]
2890		pub fn bond_extra_other(
2891			origin: OriginFor<T>,
2892			member: AccountIdLookupOf<T>,
2893			extra: BondExtra<BalanceOf<T>>,
2894		) -> DispatchResult {
2895			let who = ensure_signed(origin)?;
2896			let member_account = T::Lookup::lookup(member)?;
2897			// ensure member is not in an un-migrated state.
2898			ensure!(
2899				!Self::api_member_needs_delegate_migration(member_account.clone()),
2900				Error::<T>::NotMigrated
2901			);
2902
2903			Self::do_bond_extra(who, member_account, extra)
2904		}
2905
2906		/// Allows a pool member to set a claim permission to allow or disallow permissionless
2907		/// bonding and withdrawing.
2908		///
2909		/// # Arguments
2910		///
2911		/// * `origin` - Member of a pool.
2912		/// * `permission` - The permission to be applied.
2913		#[pallet::call_index(15)]
2914		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
2915		pub fn set_claim_permission(
2916			origin: OriginFor<T>,
2917			permission: ClaimPermission,
2918		) -> DispatchResult {
2919			let who = ensure_signed(origin)?;
2920			ensure!(PoolMembers::<T>::contains_key(&who), Error::<T>::PoolMemberNotFound);
2921
2922			// ensure member is not in an un-migrated state.
2923			ensure!(
2924				!Self::api_member_needs_delegate_migration(who.clone()),
2925				Error::<T>::NotMigrated
2926			);
2927
2928			ClaimPermissions::<T>::mutate(who.clone(), |source| {
2929				*source = permission;
2930			});
2931
2932			Self::deposit_event(Event::<T>::MemberClaimPermissionUpdated {
2933				member: who,
2934				permission,
2935			});
2936
2937			Ok(())
2938		}
2939
2940		/// `origin` can claim payouts on some pool member `other`'s behalf.
2941		///
2942		/// Pool member `other` must have a `PermissionlessWithdraw` or `PermissionlessAll` claim
2943		/// permission for this call to be successful.
2944		#[pallet::call_index(16)]
2945		#[pallet::weight(T::WeightInfo::claim_payout())]
2946		pub fn claim_payout_other(origin: OriginFor<T>, other: T::AccountId) -> DispatchResult {
2947			let signer = ensure_signed(origin)?;
2948			// ensure member is not in an un-migrated state.
2949			ensure!(
2950				!Self::api_member_needs_delegate_migration(other.clone()),
2951				Error::<T>::NotMigrated
2952			);
2953
2954			Self::do_claim_payout(signer, other)
2955		}
2956
2957		/// Set the commission of a pool.
2958		//
2959		/// Both a commission percentage and a commission payee must be provided in the `current`
2960		/// tuple. Where a `current` of `None` is provided, any current commission will be removed.
2961		///
2962		/// - If a `None` is supplied to `new_commission`, existing commission will be removed.
2963		#[pallet::call_index(17)]
2964		#[pallet::weight(T::WeightInfo::set_commission())]
2965		pub fn set_commission(
2966			origin: OriginFor<T>,
2967			pool_id: PoolId,
2968			new_commission: Option<(Perbill, T::AccountId)>,
2969		) -> DispatchResult {
2970			let who = ensure_signed(origin)?;
2971			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
2972			// ensure pool is not in an un-migrated state.
2973			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
2974
2975			ensure!(bonded_pool.can_manage_commission(&who), Error::<T>::DoesNotHavePermission);
2976
2977			let mut reward_pool = RewardPools::<T>::get(pool_id)
2978				.defensive_ok_or::<Error<T>>(DefensiveError::RewardPoolNotFound.into())?;
2979			// IMPORTANT: make sure that everything up to this point is using the current commission
2980			// before it updates. Note that `try_update_current` could still fail at this point.
2981			reward_pool.update_records(
2982				pool_id,
2983				bonded_pool.points,
2984				bonded_pool.commission.current(),
2985			)?;
2986			RewardPools::insert(pool_id, reward_pool);
2987
2988			bonded_pool.commission.try_update_current(&new_commission)?;
2989			bonded_pool.put();
2990			Self::deposit_event(Event::<T>::PoolCommissionUpdated {
2991				pool_id,
2992				current: new_commission,
2993			});
2994			Ok(())
2995		}
2996
2997		/// Set the maximum commission of a pool.
2998		///
2999		/// - Initial max can be set to any `Perbill`, and only smaller values thereafter.
3000		/// - Current commission will be lowered in the event it is higher than a new max
3001		///   commission.
3002		#[pallet::call_index(18)]
3003		#[pallet::weight(T::WeightInfo::set_commission_max())]
3004		pub fn set_commission_max(
3005			origin: OriginFor<T>,
3006			pool_id: PoolId,
3007			max_commission: Perbill,
3008		) -> DispatchResult {
3009			let who = ensure_signed(origin)?;
3010			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
3011			// ensure pool is not in an un-migrated state.
3012			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
3013
3014			ensure!(bonded_pool.can_manage_commission(&who), Error::<T>::DoesNotHavePermission);
3015
3016			let mut reward_pool = RewardPools::<T>::get(pool_id)
3017				.defensive_ok_or::<Error<T>>(DefensiveError::RewardPoolNotFound.into())?;
3018			// IMPORTANT: snapshot rewards accrued at the current commission before `try_update_max`
3019			// can force-lower it. Otherwise rewards accrued since the last snapshot would be
3020			// re-rated at the new (lower) rate and the differential credited to members instead
3021			// of the commission payee. Mirrors the ordering in `set_commission`.
3022			reward_pool.update_records(
3023				pool_id,
3024				bonded_pool.points,
3025				bonded_pool.commission.current(),
3026			)?;
3027			RewardPools::insert(pool_id, reward_pool);
3028
3029			bonded_pool.commission.try_update_max(pool_id, max_commission)?;
3030			bonded_pool.put();
3031
3032			Self::deposit_event(Event::<T>::PoolMaxCommissionUpdated { pool_id, max_commission });
3033			Ok(())
3034		}
3035
3036		/// Set the commission change rate for a pool.
3037		///
3038		/// Initial change rate is not bounded, whereas subsequent updates can only be more
3039		/// restrictive than the current.
3040		#[pallet::call_index(19)]
3041		#[pallet::weight(T::WeightInfo::set_commission_change_rate())]
3042		pub fn set_commission_change_rate(
3043			origin: OriginFor<T>,
3044			pool_id: PoolId,
3045			change_rate: CommissionChangeRate<BlockNumberFor<T>>,
3046		) -> DispatchResult {
3047			let who = ensure_signed(origin)?;
3048			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
3049			// ensure pool is not in an un-migrated state.
3050			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
3051			ensure!(bonded_pool.can_manage_commission(&who), Error::<T>::DoesNotHavePermission);
3052
3053			bonded_pool.commission.try_update_change_rate(change_rate)?;
3054			bonded_pool.put();
3055
3056			Self::deposit_event(Event::<T>::PoolCommissionChangeRateUpdated {
3057				pool_id,
3058				change_rate,
3059			});
3060			Ok(())
3061		}
3062
3063		/// Claim pending commission.
3064		///
3065		/// The `root` role of the pool is _always_ allowed to claim the pool's commission.
3066		///
3067		/// If the pool has set `CommissionClaimPermission::Permissionless`, then any account can
3068		/// trigger the process of claiming the pool's commission.
3069		///
3070		/// If the pool has set its `CommissionClaimPermission` to `Account(acc)`, then only
3071		/// accounts
3072		/// * `acc`, and
3073		/// * the pool's root account
3074		///
3075		/// may call this extrinsic on behalf of the pool.
3076		///
3077		/// Pending commissions are paid out and added to the total claimed commission.
3078		/// The total pending commission is reset to zero.
3079		#[pallet::call_index(20)]
3080		#[pallet::weight(T::WeightInfo::claim_commission())]
3081		pub fn claim_commission(origin: OriginFor<T>, pool_id: PoolId) -> DispatchResult {
3082			let who = ensure_signed(origin)?;
3083			// ensure pool is not in an un-migrated state.
3084			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
3085
3086			Self::do_claim_commission(who, pool_id)
3087		}
3088
3089		/// Top up the deficit or withdraw the excess ED from the pool.
3090		///
3091		/// When a pool is created, the pool depositor transfers ED to the reward account of the
3092		/// pool. ED is subject to change and over time, the deposit in the reward account may be
3093		/// insufficient to cover the ED deficit of the pool or vice-versa where there is excess
3094		/// deposit to the pool. This call allows anyone to adjust the ED deposit of the
3095		/// pool by either topping up the deficit or claiming the excess.
3096		#[pallet::call_index(21)]
3097		#[pallet::weight(T::WeightInfo::adjust_pool_deposit())]
3098		pub fn adjust_pool_deposit(origin: OriginFor<T>, pool_id: PoolId) -> DispatchResult {
3099			let who = ensure_signed(origin)?;
3100			// ensure pool is not in an un-migrated state.
3101			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
3102
3103			Self::do_adjust_pool_deposit(who, pool_id)
3104		}
3105
3106		/// Set or remove a pool's commission claim permission.
3107		///
3108		/// Determines who can claim the pool's pending commission. Only the `Root` role of the pool
3109		/// is able to configure commission claim permissions.
3110		#[pallet::call_index(22)]
3111		#[pallet::weight(T::WeightInfo::set_commission_claim_permission())]
3112		pub fn set_commission_claim_permission(
3113			origin: OriginFor<T>,
3114			pool_id: PoolId,
3115			permission: Option<CommissionClaimPermission<T::AccountId>>,
3116		) -> DispatchResult {
3117			let who = ensure_signed(origin)?;
3118			let mut bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
3119			// ensure pool is not in an un-migrated state.
3120			ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::<T>::NotMigrated);
3121			ensure!(bonded_pool.can_manage_commission(&who), Error::<T>::DoesNotHavePermission);
3122
3123			bonded_pool.commission.claim_permission = permission.clone();
3124			bonded_pool.put();
3125
3126			Self::deposit_event(Event::<T>::PoolCommissionClaimPermissionUpdated {
3127				pool_id,
3128				permission,
3129			});
3130
3131			Ok(())
3132		}
3133
3134		/// Apply a pending slash on a member.
3135		///
3136		/// Fails unless [`crate::pallet::Config::StakeAdapter`] is of strategy type:
3137		/// [`adapter::StakeStrategyType::Delegate`].
3138		///
3139		/// The pending slash amount of the member must be equal or more than `ExistentialDeposit`.
3140		/// This call can be dispatched permissionlessly (i.e. by any account). If the execution
3141		/// is successful, fee is refunded and caller may be rewarded with a part of the slash
3142		/// based on the [`crate::pallet::Config::StakeAdapter`] configuration.
3143		#[pallet::call_index(23)]
3144		#[pallet::weight(T::WeightInfo::apply_slash())]
3145		pub fn apply_slash(
3146			origin: OriginFor<T>,
3147			member_account: AccountIdLookupOf<T>,
3148		) -> DispatchResultWithPostInfo {
3149			ensure!(
3150				T::StakeAdapter::strategy_type() == adapter::StakeStrategyType::Delegate,
3151				Error::<T>::NotSupported
3152			);
3153
3154			let who = ensure_signed(origin)?;
3155			let member_account = T::Lookup::lookup(member_account)?;
3156			Self::do_apply_slash(&member_account, Some(who), true)?;
3157
3158			// If successful, refund the fees.
3159			Ok(Pays::No.into())
3160		}
3161
3162		/// Migrates delegated funds from the pool account to the `member_account`.
3163		///
3164		/// Fails unless [`crate::pallet::Config::StakeAdapter`] is of strategy type:
3165		/// [`adapter::StakeStrategyType::Delegate`].
3166		///
3167		/// This is a permission-less call and refunds any fee if claim is successful.
3168		///
3169		/// If the pool has migrated to delegation based staking, the staked tokens of pool members
3170		/// can be moved and held in their own account. See [`adapter::DelegateStake`]
3171		#[pallet::call_index(24)]
3172		#[pallet::weight(T::WeightInfo::migrate_delegation())]
3173		pub fn migrate_delegation(
3174			origin: OriginFor<T>,
3175			member_account: AccountIdLookupOf<T>,
3176		) -> DispatchResultWithPostInfo {
3177			let _caller = ensure_signed(origin)?;
3178
3179			// ensure `DelegateStake` strategy is used.
3180			ensure!(
3181				T::StakeAdapter::strategy_type() == adapter::StakeStrategyType::Delegate,
3182				Error::<T>::NotSupported
3183			);
3184
3185			// ensure member is not restricted from joining the pool.
3186			let member_account = T::Lookup::lookup(member_account)?;
3187			ensure!(!T::Filter::contains(&member_account), Error::<T>::Restricted);
3188
3189			let member =
3190				PoolMembers::<T>::get(&member_account).ok_or(Error::<T>::PoolMemberNotFound)?;
3191
3192			// ensure pool is migrated.
3193			ensure!(
3194				T::StakeAdapter::pool_strategy(Pool::from(Self::generate_bonded_account(
3195					member.pool_id
3196				))) == adapter::StakeStrategyType::Delegate,
3197				Error::<T>::NotMigrated
3198			);
3199
3200			let pool_contribution = member.total_balance();
3201			// ensure the pool contribution is greater than the existential deposit otherwise we
3202			// cannot transfer funds to member account.
3203			ensure!(
3204				pool_contribution >= T::Currency::minimum_balance(),
3205				Error::<T>::MinimumBondNotMet
3206			);
3207
3208			let delegation =
3209				T::StakeAdapter::member_delegation_balance(Member::from(member_account.clone()));
3210			// delegation should not exist.
3211			ensure!(delegation.is_none(), Error::<T>::AlreadyMigrated);
3212
3213			T::StakeAdapter::migrate_delegation(
3214				Pool::from(Pallet::<T>::generate_bonded_account(member.pool_id)),
3215				Member::from(member_account),
3216				pool_contribution,
3217			)?;
3218
3219			// if successful, we refund the fee.
3220			Ok(Pays::No.into())
3221		}
3222
3223		/// Migrate pool from [`adapter::StakeStrategyType::Transfer`] to
3224		/// [`adapter::StakeStrategyType::Delegate`].
3225		///
3226		/// Fails unless [`crate::pallet::Config::StakeAdapter`] is of strategy type:
3227		/// [`adapter::StakeStrategyType::Delegate`].
3228		///
3229		/// This call can be dispatched permissionlessly, and refunds any fee if successful.
3230		///
3231		/// If the pool has already migrated to delegation based staking, this call will fail.
3232		#[pallet::call_index(25)]
3233		#[pallet::weight(T::WeightInfo::pool_migrate())]
3234		pub fn migrate_pool_to_delegate_stake(
3235			origin: OriginFor<T>,
3236			pool_id: PoolId,
3237		) -> DispatchResultWithPostInfo {
3238			// gate this call to be called only if `DelegateStake` strategy is used.
3239			ensure!(
3240				T::StakeAdapter::strategy_type() == adapter::StakeStrategyType::Delegate,
3241				Error::<T>::NotSupported
3242			);
3243
3244			let _caller = ensure_signed(origin)?;
3245			// ensure pool exists.
3246			let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
3247			ensure!(
3248				T::StakeAdapter::pool_strategy(Pool::from(bonded_pool.bonded_account())) ==
3249					adapter::StakeStrategyType::Transfer,
3250				Error::<T>::AlreadyMigrated
3251			);
3252
3253			Self::migrate_to_delegate_stake(pool_id)?;
3254			Ok(Pays::No.into())
3255		}
3256	}
3257
3258	#[pallet::hooks]
3259	impl<T: Config> Hooks<SystemBlockNumberFor<T>> for Pallet<T> {
3260		#[cfg(feature = "try-runtime")]
3261		fn try_state(_n: SystemBlockNumberFor<T>) -> Result<(), TryRuntimeError> {
3262			Self::do_try_state(u8::MAX)
3263		}
3264
3265		fn integrity_test() {
3266			assert!(
3267				T::MaxPointsToBalance::get() > 0,
3268				"Minimum points to balance ratio must be greater than 0"
3269			);
3270			assert!(
3271				T::StakeAdapter::bonding_duration() < T::MaxUnbondingPools::get(),
3272				"There must be more unbonding pools then the bonding duration /
3273				so a slash can be applied to relevant unbonding pools. (We assume /
3274				the bonding duration > slash deffer duration.",
3275			);
3276		}
3277	}
3278}
3279
3280impl<T: Config> Pallet<T> {
3281	/// The amount of bond that MUST REMAIN IN BONDED in ALL POOLS.
3282	///
3283	/// It is the responsibility of the depositor to put these funds into the pool initially. Upon
3284	/// unbond, they can never unbond to a value below this amount.
3285	///
3286	/// It is essentially `max { MinNominatorBond, MinCreateBond, MinJoinBond }`, where the former
3287	/// is coming from the staking pallet and the latter two are configured in this pallet.
3288	pub fn depositor_min_bond() -> BalanceOf<T> {
3289		T::StakeAdapter::minimum_nominator_bond()
3290			.max(MinCreateBond::<T>::get())
3291			.max(MinJoinBond::<T>::get())
3292			.max(T::Currency::minimum_balance())
3293	}
3294
3295	/// Claim trapped balance for a pool member.
3296	///
3297	/// In rare scenarios, pool members may have excess held balance that is not accounted
3298	/// for in their pool points. This can occur when points are incorrectly dissolved
3299	/// without releasing the corresponding held funds.
3300	///
3301	/// If the pool has any pending slash, it will be applied to the member first before
3302	/// claiming the trapped balance.
3303	///
3304	/// Safe to call multiple times or for non-existent members — returns `Ok(())` as a
3305	/// no-op when there is nothing to do.
3306	pub fn do_claim_trapped_balance(member_account: &T::AccountId) -> DispatchResult {
3307		ensure!(
3308			T::StakeAdapter::strategy_type() == adapter::StakeStrategyType::Delegate,
3309			Error::<T>::NotSupported
3310		);
3311
3312		// Apply any pending slash first. Ignore NothingToSlash and PoolMemberNotFound
3313		// (member existence is validated below).
3314		match Self::do_apply_slash(member_account, None, false) {
3315			Ok(_) => {},
3316			Err(e)
3317				if e == Error::<T>::NothingToSlash.into() ||
3318					e == Error::<T>::PoolMemberNotFound.into() => {},
3319			Err(_) => {
3320				return Err(Error::<T>::Defensive(DefensiveError::SlashNotApplied).into());
3321			},
3322		};
3323
3324		let member = match PoolMembers::<T>::get(member_account) {
3325			Some(m) => m,
3326			None => return Ok(()),
3327		};
3328
3329		let expected_balance = member.total_balance();
3330		let actual_balance =
3331			T::StakeAdapter::member_delegation_balance(Member::from(member_account.clone()))
3332				.unwrap_or_default();
3333
3334		let trapped_amount = actual_balance.saturating_sub(expected_balance);
3335
3336		if trapped_amount.is_zero() {
3337			return Ok(());
3338		}
3339
3340		T::StakeAdapter::member_withdraw(
3341			Member::from(member_account.clone()),
3342			Pool::from(Self::generate_bonded_account(member.pool_id)),
3343			trapped_amount,
3344			0,
3345		)?;
3346
3347		log!(
3348			info,
3349			"Claimed trapped balance for member {:?}, pool {:?}, amount {:?}",
3350			member_account,
3351			member.pool_id,
3352			trapped_amount
3353		);
3354
3355		Ok(())
3356	}
3357
3358	/// Remove everything related to the given bonded pool.
3359	///
3360	/// Metadata and all of the sub-pools are also deleted. All accounts are dusted and the leftover
3361	/// of the reward account is returned to the depositor.
3362	pub fn dissolve_pool(bonded_pool: BondedPool<T>) {
3363		let reward_account = bonded_pool.reward_account();
3364		let bonded_account = bonded_pool.bonded_account();
3365
3366		ReversePoolIdLookup::<T>::remove(&bonded_account);
3367		RewardPools::<T>::remove(bonded_pool.id);
3368		SubPoolsStorage::<T>::remove(bonded_pool.id);
3369
3370		// remove the ED restriction from the pool reward account.
3371		let _ = Self::unfreeze_pool_deposit(&bonded_pool.reward_account()).defensive();
3372
3373		// Kill accounts from storage by making their balance go below ED. We assume that the
3374		// accounts have no references that would prevent destruction once we get to this point. We
3375		// don't work with the system pallet directly, but
3376		// 1. we drain the reward account and kill it. This account should never have any extra
3377		// consumers anyway.
3378		// 2. the bonded account should become a 'killed stash' in the staking system, and all of
3379		//    its consumers removed.
3380		defensive_assert!(
3381			frame_system::Pallet::<T>::consumers(&reward_account) == 0,
3382			"reward account of dissolving pool should have no consumers"
3383		);
3384		defensive_assert!(
3385			frame_system::Pallet::<T>::consumers(&bonded_account) == 0,
3386			"bonded account of dissolving pool should have no consumers"
3387		);
3388		defensive_assert!(
3389			T::StakeAdapter::total_stake(Pool::from(bonded_pool.bonded_account())) == Zero::zero(),
3390			"dissolving pool should not have any stake in the staking pallet"
3391		);
3392
3393		// This shouldn't fail, but if it does we don't really care. Remaining balance can consist
3394		// of unclaimed pending commission, erroneous transfers to the reward account, etc.
3395		let reward_pool_remaining = T::Currency::reducible_balance(
3396			&reward_account,
3397			Preservation::Expendable,
3398			Fortitude::Polite,
3399		);
3400		let _ = T::Currency::transfer(
3401			&reward_account,
3402			&bonded_pool.roles.depositor,
3403			reward_pool_remaining,
3404			Preservation::Expendable,
3405		);
3406
3407		defensive_assert!(
3408			T::Currency::total_balance(&reward_account) == Zero::zero(),
3409			"could not transfer all amount to depositor while dissolving pool"
3410		);
3411		// NOTE: Defensively force set balance to zero.
3412		T::Currency::set_balance(&reward_account, Zero::zero());
3413
3414		// dissolve pool account.
3415		let _ = T::StakeAdapter::dissolve(Pool::from(bonded_account)).defensive();
3416
3417		Self::deposit_event(Event::<T>::Destroyed { pool_id: bonded_pool.id });
3418		// Remove bonded pool metadata.
3419		Metadata::<T>::remove(bonded_pool.id);
3420
3421		bonded_pool.remove();
3422	}
3423
3424	/// Create the main, bonded account of a pool with the given id.
3425	pub fn generate_bonded_account(id: PoolId) -> T::AccountId {
3426		T::PalletId::get().into_sub_account_truncating((AccountType::Bonded, id))
3427	}
3428
3429	fn migrate_to_delegate_stake(id: PoolId) -> DispatchResult {
3430		T::StakeAdapter::migrate_nominator_to_agent(
3431			Pool::from(Self::generate_bonded_account(id)),
3432			&Self::generate_reward_account(id),
3433		)
3434	}
3435
3436	/// Create the reward account of a pool with the given id.
3437	pub fn generate_reward_account(id: PoolId) -> T::AccountId {
3438		// NOTE: in order to have a distinction in the test account id type (u128), we put
3439		// account_type first so it does not get truncated out.
3440		T::PalletId::get().into_sub_account_truncating((AccountType::Reward, id))
3441	}
3442
3443	/// Get the member with their associated bonded and reward pool.
3444	fn get_member_with_pools(
3445		who: &T::AccountId,
3446	) -> Result<(PoolMember<T>, BondedPool<T>, RewardPool<T>), Error<T>> {
3447		let member = PoolMembers::<T>::get(who).ok_or(Error::<T>::PoolMemberNotFound)?;
3448		let bonded_pool =
3449			BondedPool::<T>::get(member.pool_id).defensive_ok_or(DefensiveError::PoolNotFound)?;
3450		let reward_pool =
3451			RewardPools::<T>::get(member.pool_id).defensive_ok_or(DefensiveError::PoolNotFound)?;
3452		Ok((member, bonded_pool, reward_pool))
3453	}
3454
3455	/// Persist the member with their associated bonded and reward pool into storage, consuming
3456	/// all of them.
3457	fn put_member_with_pools(
3458		member_account: &T::AccountId,
3459		member: PoolMember<T>,
3460		bonded_pool: BondedPool<T>,
3461		reward_pool: RewardPool<T>,
3462	) {
3463		// The pool id of a member cannot change in any case, so we use it to make sure
3464		// `member_account` is the right one.
3465		debug_assert_eq!(PoolMembers::<T>::get(member_account).unwrap().pool_id, member.pool_id);
3466		debug_assert_eq!(member.pool_id, bonded_pool.id);
3467
3468		bonded_pool.put();
3469		RewardPools::insert(member.pool_id, reward_pool);
3470		PoolMembers::<T>::insert(member_account, member);
3471	}
3472
3473	/// Calculate the equivalent point of `new_funds` in a pool with `current_balance` and
3474	/// `current_points`.
3475	fn balance_to_point(
3476		current_balance: BalanceOf<T>,
3477		current_points: BalanceOf<T>,
3478		new_funds: BalanceOf<T>,
3479	) -> BalanceOf<T> {
3480		let u256 = T::BalanceToU256::convert;
3481		let balance = T::U256ToBalance::convert;
3482		match (current_balance.is_zero(), current_points.is_zero()) {
3483			(_, true) => new_funds.saturating_mul(POINTS_TO_BALANCE_INIT_RATIO.into()),
3484			(true, false) => {
3485				// The pool was totally slashed.
3486				// This is the equivalent of `(current_points / 1) * new_funds`.
3487				new_funds.saturating_mul(current_points)
3488			},
3489			(false, false) => {
3490				// Equivalent to (current_points / current_balance) * new_funds
3491				balance(
3492					u256(current_points)
3493						.saturating_mul(u256(new_funds))
3494						// We check for zero above
3495						.div(u256(current_balance)),
3496				)
3497			},
3498		}
3499	}
3500
3501	/// Calculate the equivalent balance of `points` in a pool with `current_balance` and
3502	/// `current_points`.
3503	fn point_to_balance(
3504		current_balance: BalanceOf<T>,
3505		current_points: BalanceOf<T>,
3506		points: BalanceOf<T>,
3507	) -> BalanceOf<T> {
3508		let u256 = T::BalanceToU256::convert;
3509		let balance = T::U256ToBalance::convert;
3510		if current_balance.is_zero() || current_points.is_zero() || points.is_zero() {
3511			// There is nothing to unbond
3512			return Zero::zero();
3513		}
3514
3515		// Equivalent of (current_balance / current_points) * points
3516		balance(
3517			u256(current_balance)
3518				.saturating_mul(u256(points))
3519				// We check for zero above
3520				.div(u256(current_points)),
3521		)
3522	}
3523
3524	/// If the member has some rewards, transfer a payout from the reward pool to the member.
3525	// Emits events and potentially modifies pool state if any arithmetic saturates, but does
3526	// not persist any of the mutable inputs to storage.
3527	fn do_reward_payout(
3528		member_account: &T::AccountId,
3529		member: &mut PoolMember<T>,
3530		bonded_pool: &mut BondedPool<T>,
3531		reward_pool: &mut RewardPool<T>,
3532	) -> Result<BalanceOf<T>, DispatchError> {
3533		debug_assert_eq!(member.pool_id, bonded_pool.id);
3534		debug_assert_eq!(&mut PoolMembers::<T>::get(member_account).unwrap(), member);
3535
3536		// a member who has no skin in the game anymore cannot claim any rewards.
3537		ensure!(!member.active_points().is_zero(), Error::<T>::FullyUnbonding);
3538
3539		let (current_reward_counter, _) = reward_pool.current_reward_counter(
3540			bonded_pool.id,
3541			bonded_pool.points,
3542			bonded_pool.commission.current(),
3543		)?;
3544
3545		// Determine the pending rewards. In scenarios where commission is 100%, `pending_rewards`
3546		// will be zero.
3547		let pending_rewards = member.pending_rewards(current_reward_counter)?;
3548		if pending_rewards.is_zero() {
3549			return Ok(pending_rewards);
3550		}
3551
3552		// IFF the reward is non-zero alter the member and reward pool info.
3553		member.last_recorded_reward_counter = current_reward_counter;
3554		reward_pool.register_claimed_reward(pending_rewards);
3555
3556		T::Currency::transfer(
3557			&bonded_pool.reward_account(),
3558			member_account,
3559			pending_rewards,
3560			// defensive: the depositor has put existential deposit into the pool and it stays
3561			// untouched, reward account shall not die.
3562			Preservation::Preserve,
3563		)?;
3564
3565		Self::deposit_event(Event::<T>::PaidOut {
3566			member: member_account.clone(),
3567			pool_id: member.pool_id,
3568			payout: pending_rewards,
3569		});
3570		Ok(pending_rewards)
3571	}
3572
3573	fn do_create(
3574		who: T::AccountId,
3575		amount: BalanceOf<T>,
3576		root: AccountIdLookupOf<T>,
3577		nominator: AccountIdLookupOf<T>,
3578		bouncer: AccountIdLookupOf<T>,
3579		pool_id: PoolId,
3580	) -> DispatchResult {
3581		// ensure depositor is not restricted from joining the pool.
3582		ensure!(!T::Filter::contains(&who), Error::<T>::Restricted);
3583
3584		let root = T::Lookup::lookup(root)?;
3585		let nominator = T::Lookup::lookup(nominator)?;
3586		let bouncer = T::Lookup::lookup(bouncer)?;
3587
3588		ensure!(amount >= Pallet::<T>::depositor_min_bond(), Error::<T>::MinimumBondNotMet);
3589		ensure!(
3590			MaxPools::<T>::get().map_or(true, |max_pools| BondedPools::<T>::count() < max_pools),
3591			Error::<T>::MaxPools
3592		);
3593		ensure!(!PoolMembers::<T>::contains_key(&who), Error::<T>::AccountBelongsToOtherPool);
3594		let mut bonded_pool = BondedPool::<T>::new(
3595			pool_id,
3596			PoolRoles {
3597				root: Some(root),
3598				nominator: Some(nominator),
3599				bouncer: Some(bouncer),
3600				depositor: who.clone(),
3601			},
3602		);
3603
3604		bonded_pool.try_inc_members()?;
3605		let points = bonded_pool.try_bond_funds(&who, amount, BondType::Create)?;
3606
3607		// Transfer the minimum balance for the reward account.
3608		T::Currency::transfer(
3609			&who,
3610			&bonded_pool.reward_account(),
3611			T::Currency::minimum_balance(),
3612			Preservation::Expendable,
3613		)?;
3614
3615		// Restrict reward account balance from going below ED.
3616		Self::freeze_pool_deposit(&bonded_pool.reward_account())?;
3617
3618		PoolMembers::<T>::insert(
3619			who.clone(),
3620			PoolMember::<T> {
3621				pool_id,
3622				points,
3623				last_recorded_reward_counter: Zero::zero(),
3624				unbonding_eras: Default::default(),
3625			},
3626		);
3627		RewardPools::<T>::insert(
3628			pool_id,
3629			RewardPool::<T> {
3630				last_recorded_reward_counter: Zero::zero(),
3631				last_recorded_total_payouts: Zero::zero(),
3632				total_rewards_claimed: Zero::zero(),
3633				total_commission_pending: Zero::zero(),
3634				total_commission_claimed: Zero::zero(),
3635			},
3636		);
3637		ReversePoolIdLookup::<T>::insert(bonded_pool.bonded_account(), pool_id);
3638
3639		Self::deposit_event(Event::<T>::Created { depositor: who.clone(), pool_id });
3640
3641		Self::deposit_event(Event::<T>::Bonded {
3642			member: who,
3643			pool_id,
3644			bonded: amount,
3645			joined: true,
3646		});
3647		bonded_pool.put();
3648
3649		Ok(())
3650	}
3651
3652	fn do_bond_extra(
3653		signer: T::AccountId,
3654		member_account: T::AccountId,
3655		extra: BondExtra<BalanceOf<T>>,
3656	) -> DispatchResult {
3657		// ensure account is not restricted from joining the pool.
3658		ensure!(!T::Filter::contains(&member_account), Error::<T>::Restricted);
3659
3660		if signer != member_account {
3661			ensure!(
3662				ClaimPermissions::<T>::get(&member_account).can_bond_extra(),
3663				Error::<T>::DoesNotHavePermission
3664			);
3665			ensure!(extra == BondExtra::Rewards, Error::<T>::BondExtraRestricted);
3666		}
3667
3668		let (mut member, mut bonded_pool, mut reward_pool) =
3669			Self::get_member_with_pools(&member_account)?;
3670
3671		// payout related stuff: we must claim the payouts, and updated recorded payout data
3672		// before updating the bonded pool points, similar to that of `join` transaction.
3673		reward_pool.update_records(
3674			bonded_pool.id,
3675			bonded_pool.points,
3676			bonded_pool.commission.current(),
3677		)?;
3678		let claimed = Self::do_reward_payout(
3679			&member_account,
3680			&mut member,
3681			&mut bonded_pool,
3682			&mut reward_pool,
3683		)?;
3684
3685		let (points_issued, bonded) = match extra {
3686			BondExtra::FreeBalance(amount) => {
3687				(bonded_pool.try_bond_funds(&member_account, amount, BondType::Extra)?, amount)
3688			},
3689			BondExtra::Rewards => {
3690				(bonded_pool.try_bond_funds(&member_account, claimed, BondType::Extra)?, claimed)
3691			},
3692		};
3693
3694		bonded_pool.ok_to_be_open()?;
3695		member.points =
3696			member.points.checked_add(&points_issued).ok_or(Error::<T>::OverflowRisk)?;
3697
3698		Self::deposit_event(Event::<T>::Bonded {
3699			member: member_account.clone(),
3700			pool_id: member.pool_id,
3701			bonded,
3702			joined: false,
3703		});
3704		Self::put_member_with_pools(&member_account, member, bonded_pool, reward_pool);
3705
3706		Ok(())
3707	}
3708
3709	fn do_claim_commission(who: T::AccountId, pool_id: PoolId) -> DispatchResult {
3710		let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
3711		ensure!(bonded_pool.can_claim_commission(&who), Error::<T>::DoesNotHavePermission);
3712
3713		let mut reward_pool = RewardPools::<T>::get(pool_id)
3714			.defensive_ok_or::<Error<T>>(DefensiveError::RewardPoolNotFound.into())?;
3715
3716		// IMPORTANT: ensure newly pending commission not yet processed is added to
3717		// `total_commission_pending`.
3718		reward_pool.update_records(
3719			pool_id,
3720			bonded_pool.points,
3721			bonded_pool.commission.current(),
3722		)?;
3723
3724		let commission = reward_pool.total_commission_pending;
3725		ensure!(!commission.is_zero(), Error::<T>::NoPendingCommission);
3726
3727		let payee = bonded_pool
3728			.commission
3729			.current
3730			.as_ref()
3731			.map(|(_, p)| p.clone())
3732			.ok_or(Error::<T>::NoCommissionCurrentSet)?;
3733
3734		// Payout claimed commission.
3735		T::Currency::transfer(
3736			&bonded_pool.reward_account(),
3737			&payee,
3738			commission,
3739			Preservation::Preserve,
3740		)?;
3741
3742		// Add pending commission to total claimed counter.
3743		reward_pool.total_commission_claimed =
3744			reward_pool.total_commission_claimed.saturating_add(commission);
3745		// Reset total pending commission counter to zero.
3746		reward_pool.total_commission_pending = Zero::zero();
3747		RewardPools::<T>::insert(pool_id, reward_pool);
3748
3749		Self::deposit_event(Event::<T>::PoolCommissionClaimed { pool_id, commission });
3750		Ok(())
3751	}
3752
3753	pub(crate) fn do_claim_payout(
3754		signer: T::AccountId,
3755		member_account: T::AccountId,
3756	) -> DispatchResult {
3757		if signer != member_account {
3758			ensure!(
3759				ClaimPermissions::<T>::get(&member_account).can_claim_payout(),
3760				Error::<T>::DoesNotHavePermission
3761			);
3762		}
3763		let (mut member, mut bonded_pool, mut reward_pool) =
3764			Self::get_member_with_pools(&member_account)?;
3765
3766		Self::do_reward_payout(&member_account, &mut member, &mut bonded_pool, &mut reward_pool)?;
3767
3768		Self::put_member_with_pools(&member_account, member, bonded_pool, reward_pool);
3769		Ok(())
3770	}
3771
3772	fn do_adjust_pool_deposit(who: T::AccountId, pool: PoolId) -> DispatchResult {
3773		let bonded_pool = BondedPool::<T>::get(pool).ok_or(Error::<T>::PoolNotFound)?;
3774
3775		let reward_acc = &bonded_pool.reward_account();
3776		let pre_frozen_balance =
3777			T::Currency::balance_frozen(&FreezeReason::PoolMinBalance.into(), reward_acc);
3778		let min_balance = T::Currency::minimum_balance();
3779
3780		if pre_frozen_balance == min_balance {
3781			return Err(Error::<T>::NothingToAdjust.into());
3782		}
3783
3784		// Update frozen amount with current ED.
3785		Self::freeze_pool_deposit(reward_acc)?;
3786
3787		if pre_frozen_balance > min_balance {
3788			// Ensure the caller is the depositor or the root.
3789			ensure!(
3790				who == bonded_pool.roles.depositor ||
3791					bonded_pool.roles.root.as_ref().map_or(false, |root| &who == root),
3792				Error::<T>::DoesNotHavePermission
3793			);
3794
3795			// Transfer excess back to depositor.
3796			let excess = pre_frozen_balance.saturating_sub(min_balance);
3797
3798			T::Currency::transfer(reward_acc, &who, excess, Preservation::Preserve)?;
3799			Self::deposit_event(Event::<T>::MinBalanceExcessAdjusted {
3800				pool_id: pool,
3801				amount: excess,
3802			});
3803		} else {
3804			// Transfer ED deficit from depositor to the pool
3805			let deficit = min_balance.saturating_sub(pre_frozen_balance);
3806			T::Currency::transfer(&who, reward_acc, deficit, Preservation::Expendable)?;
3807			Self::deposit_event(Event::<T>::MinBalanceDeficitAdjusted {
3808				pool_id: pool,
3809				amount: deficit,
3810			});
3811		}
3812
3813		Ok(())
3814	}
3815
3816	/// Slash member against the pending slash for the pool.
3817	fn do_apply_slash(
3818		member_account: &T::AccountId,
3819		reporter: Option<T::AccountId>,
3820		enforce_min_slash: bool,
3821	) -> DispatchResult {
3822		let member = PoolMembers::<T>::get(member_account).ok_or(Error::<T>::PoolMemberNotFound)?;
3823
3824		let pending_slash =
3825			Self::member_pending_slash(Member::from(member_account.clone()), member.clone())?;
3826
3827		// ensure there is something to slash.
3828		ensure!(!pending_slash.is_zero(), Error::<T>::NothingToSlash);
3829
3830		if enforce_min_slash {
3831			// ensure slashed amount is at least the minimum balance.
3832			ensure!(pending_slash >= T::Currency::minimum_balance(), Error::<T>::SlashTooLow);
3833		}
3834
3835		T::StakeAdapter::member_slash(
3836			Member::from(member_account.clone()),
3837			Pool::from(Pallet::<T>::generate_bonded_account(member.pool_id)),
3838			pending_slash,
3839			reporter,
3840		)
3841	}
3842
3843	/// Pending slash for a member.
3844	///
3845	/// Takes the pool_member object corresponding to the `member_account`.
3846	fn member_pending_slash(
3847		member_account: Member<T::AccountId>,
3848		pool_member: PoolMember<T>,
3849	) -> Result<BalanceOf<T>, DispatchError> {
3850		// only executed in tests: ensure the member account is correct.
3851		debug_assert!(
3852			PoolMembers::<T>::get(member_account.clone().get()).expect("member must exist") ==
3853				pool_member
3854		);
3855
3856		let pool_account = Pallet::<T>::generate_bonded_account(pool_member.pool_id);
3857		// if the pool doesn't have any pending slash, it implies the member also does not have any
3858		// pending slash.
3859		if T::StakeAdapter::pending_slash(Pool::from(pool_account.clone())).is_zero() {
3860			return Ok(Zero::zero());
3861		}
3862
3863		// this is their actual held balance that may or may not have been slashed.
3864		let actual_balance = T::StakeAdapter::member_delegation_balance(member_account)
3865			// no delegation implies the member delegation is not migrated yet to `DelegateStake`.
3866			.ok_or(Error::<T>::NotMigrated)?;
3867
3868		// this is their balance in the pool
3869		let expected_balance = pool_member.total_balance();
3870
3871		// return the amount to be slashed.
3872		Ok(actual_balance.saturating_sub(expected_balance))
3873	}
3874
3875	/// Apply freeze on reward account to restrict it from going below ED.
3876	pub(crate) fn freeze_pool_deposit(reward_acc: &T::AccountId) -> DispatchResult {
3877		T::Currency::set_freeze(
3878			&FreezeReason::PoolMinBalance.into(),
3879			reward_acc,
3880			T::Currency::minimum_balance(),
3881		)
3882	}
3883
3884	/// Removes the ED freeze on the reward account of `pool_id`.
3885	pub fn unfreeze_pool_deposit(reward_acc: &T::AccountId) -> DispatchResult {
3886		T::Currency::thaw(&FreezeReason::PoolMinBalance.into(), reward_acc)
3887	}
3888
3889	/// Ensure the correctness of the state of this pallet.
3890	///
3891	/// This should be valid before or after each state transition of this pallet.
3892	///
3893	/// ## Invariants:
3894	///
3895	/// First, let's consider pools:
3896	///
3897	/// * `BondedPools` and `RewardPools` must all have the EXACT SAME key-set.
3898	/// * `SubPoolsStorage` must be a subset of the above superset.
3899	/// * `Metadata` keys must be a subset of the above superset.
3900	/// * the count of the above set must be less than `MaxPools`.
3901	///
3902	/// Then, considering members as well:
3903	///
3904	/// * each `BondedPool.member_counter` must be:
3905	///   - correct (compared to actual count of member who have `.pool_id` this pool)
3906	///   - less than `MaxPoolMembersPerPool`.
3907	/// * each `member.pool_id` must correspond to an existing `BondedPool.id` (which implies the
3908	///   existence of the reward pool as well).
3909	/// * count of all members must be less than `MaxPoolMembers`.
3910	/// * each `BondedPool.points` must never be lower than the pool's balance.
3911	///
3912	/// Then, considering unbonding members:
3913	///
3914	/// for each pool:
3915	///   * sum of the balance that's tracked in all unbonding pools must be the same as the
3916	///     unbonded balance of the main account, as reported by the staking interface.
3917	///   * sum of the balance that's tracked in all unbonding pools, plus the bonded balance of the
3918	///     main account should be less than or qual to the total balance of the main account.
3919	///
3920	/// ## Sanity check level
3921	///
3922	/// To cater for tests that want to escape parts of these checks, this function is split into
3923	/// multiple `level`s, where the higher the level, the more checks we performs. So,
3924	/// `try_state(255)` is the strongest sanity check, and `0` performs no checks.
3925	#[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))]
3926	pub fn do_try_state(level: u8) -> Result<(), TryRuntimeError> {
3927		if level.is_zero() {
3928			return Ok(());
3929		}
3930		// note: while a bit wacky, since they have the same key, even collecting to vec should
3931		// result in the same set of keys, in the same order.
3932		let bonded_pools = BondedPools::<T>::iter_keys().collect::<Vec<_>>();
3933		let reward_pools = RewardPools::<T>::iter_keys().collect::<Vec<_>>();
3934		ensure!(
3935			bonded_pools == reward_pools,
3936			"`BondedPools` and `RewardPools` must all have the EXACT SAME key-set."
3937		);
3938
3939		ensure!(
3940			SubPoolsStorage::<T>::iter_keys().all(|k| bonded_pools.contains(&k)),
3941			"`SubPoolsStorage` must be a subset of the above superset."
3942		);
3943		ensure!(
3944			Metadata::<T>::iter_keys().all(|k| bonded_pools.contains(&k)),
3945			"`Metadata` keys must be a subset of the above superset."
3946		);
3947
3948		ensure!(
3949			MaxPools::<T>::get().map_or(true, |max| bonded_pools.len() <= (max as usize)),
3950			Error::<T>::MaxPools
3951		);
3952
3953		for id in reward_pools {
3954			let account = Self::generate_reward_account(id);
3955			if T::Currency::reducible_balance(&account, Preservation::Expendable, Fortitude::Polite) <
3956				T::Currency::minimum_balance()
3957			{
3958				log!(
3959					warn,
3960					"reward pool of {:?}: {:?} (ed = {:?}), should only happen because ED has \
3961					changed recently. Pool operators should be notified to top up the reward \
3962					account",
3963					id,
3964					T::Currency::reducible_balance(
3965						&account,
3966						Preservation::Expendable,
3967						Fortitude::Polite
3968					),
3969					T::Currency::minimum_balance(),
3970				)
3971			}
3972		}
3973
3974		let mut pools_members = BTreeMap::<PoolId, u32>::new();
3975		let mut pools_members_pending_rewards = BTreeMap::<PoolId, BalanceOf<T>>::new();
3976		let mut all_members = 0u32;
3977		let mut total_balance_members = Default::default();
3978		PoolMembers::<T>::iter().try_for_each(|(_, d)| -> Result<(), TryRuntimeError> {
3979			let bonded_pool = BondedPools::<T>::get(d.pool_id).unwrap();
3980			ensure!(!d.total_points().is_zero(), "No member should have zero points");
3981			*pools_members.entry(d.pool_id).or_default() += 1;
3982			all_members += 1;
3983
3984			let reward_pool = RewardPools::<T>::get(d.pool_id).unwrap();
3985			if !bonded_pool.points.is_zero() {
3986				let commission = bonded_pool.commission.current();
3987				let (current_rc, _) = reward_pool
3988					.current_reward_counter(d.pool_id, bonded_pool.points, commission)
3989					.unwrap();
3990				let pending_rewards = d.pending_rewards(current_rc).unwrap();
3991				*pools_members_pending_rewards.entry(d.pool_id).or_default() += pending_rewards;
3992			} // else this pool has been heavily slashed and cannot have any rewards anymore.
3993			total_balance_members += d.total_balance();
3994
3995			Ok(())
3996		})?;
3997
3998		RewardPools::<T>::iter_keys().try_for_each(|id| -> Result<(), TryRuntimeError> {
3999			// the sum of the pending rewards must be less than the leftover balance. Since the
4000			// reward math rounds down, we might accumulate some dust here.
4001			let pending_rewards_lt_leftover_bal = RewardPool::<T>::current_balance(id) >=
4002				pools_members_pending_rewards.get(&id).copied().unwrap_or_default();
4003
4004			// If this happens, this is most likely due to an old bug and not a recent code change.
4005			// We warn about this in try-runtime checks but do not panic.
4006			if !pending_rewards_lt_leftover_bal {
4007				log!(
4008					warn,
4009					"pool {:?}, sum pending rewards = {:?}, remaining balance = {:?}",
4010					id,
4011					pools_members_pending_rewards.get(&id),
4012					RewardPool::<T>::current_balance(id)
4013				);
4014			}
4015			Ok(())
4016		})?;
4017
4018		let mut expected_tvl: BalanceOf<T> = Default::default();
4019		let mut depositor_undermin: Vec<(PoolId, T::AccountId)> = Vec::new();
4020		let mut depositor_undermin_total: u32 = 0;
4021		let mut total_pools: u32 = 0;
4022		const MAX_EXAMPLES: usize = 10;
4023
4024		BondedPools::<T>::iter().try_for_each(|(id, inner)| -> Result<(), TryRuntimeError> {
4025			total_pools += 1;
4026			let bonded_pool = BondedPool { id, inner };
4027			ensure!(
4028				pools_members.get(&id).copied().unwrap_or_default() ==
4029				bonded_pool.member_counter,
4030				"Each `BondedPool.member_counter` must be equal to the actual count of members of this pool"
4031			);
4032			ensure!(
4033				MaxPoolMembersPerPool::<T>::get()
4034					.map_or(true, |max| bonded_pool.member_counter <= max),
4035				Error::<T>::MaxPoolMembers
4036			);
4037
4038			let depositor = PoolMembers::<T>::get(&bonded_pool.roles.depositor).unwrap();
4039			let depositor_has_enough_stake = bonded_pool
4040				.is_destroying_and_only_depositor(depositor.active_points()) ||
4041				depositor.active_points() >= MinCreateBond::<T>::get();
4042			if !depositor_has_enough_stake {
4043				depositor_undermin_total += 1;
4044				if depositor_undermin.len() < MAX_EXAMPLES {
4045					depositor_undermin.push((id, bonded_pool.roles.depositor.clone()));
4046				}
4047				log!(
4048					trace,
4049					"pool {:?} has depositor {:?} with insufficient stake {:?}, minimum required is {:?}",
4050					id,
4051					bonded_pool.roles.depositor,
4052					depositor.active_points(),
4053					MinCreateBond::<T>::get()
4054				);
4055			}
4056
4057			ensure!(
4058				bonded_pool.points >= bonded_pool.points_to_balance(bonded_pool.points),
4059				"Each `BondedPool.points` must never be lower than the pool's balance"
4060			);
4061
4062			expected_tvl += T::StakeAdapter::total_stake(Pool::from(bonded_pool.bonded_account()));
4063
4064			Ok(())
4065		})?;
4066
4067		if depositor_undermin_total > 0 {
4068			log!(
4069				warn,
4070				"{}/{} pools have depositor with insufficient stake, minimum required is {:?}. Examples: {:?}",
4071				depositor_undermin_total,
4072				total_pools,
4073				MinCreateBond::<T>::get(),
4074				depositor_undermin,
4075			);
4076		}
4077
4078		ensure!(
4079			MaxPoolMembers::<T>::get().map_or(true, |max| all_members <= max),
4080			Error::<T>::MaxPoolMembers
4081		);
4082
4083		ensure!(
4084			TotalValueLocked::<T>::get() == expected_tvl,
4085			"TVL deviates from the actual sum of funds of all Pools."
4086		);
4087
4088		ensure!(
4089			TotalValueLocked::<T>::get() <= total_balance_members,
4090			"TVL must be equal to or less than the total balance of all PoolMembers."
4091		);
4092
4093		if level <= 1 {
4094			return Ok(());
4095		}
4096
4097		for (pool_id, _pool) in BondedPools::<T>::iter() {
4098			let pool_account = Pallet::<T>::generate_bonded_account(pool_id);
4099			let subs = SubPoolsStorage::<T>::get(pool_id).unwrap_or_default();
4100
4101			let sum_unbonding_balance = subs.sum_unbonding_balance();
4102			let bonded_balance = T::StakeAdapter::active_stake(Pool::from(pool_account.clone()));
4103			// TODO: should be total_balance + unclaimed_withdrawals from delegated staking
4104			let total_balance = T::StakeAdapter::total_balance(Pool::from(pool_account.clone()))
4105				// At the time when StakeAdapter is changed to `DelegateStake` but pool is not yet
4106				// migrated, the total balance would be none.
4107				.unwrap_or(T::Currency::total_balance(&pool_account));
4108
4109			if total_balance < bonded_balance + sum_unbonding_balance {
4110				log!(
4111						warn,
4112						"possibly faulty pool: {:?} / {:?}, total_balance {:?} >= bonded_balance {:?} + sum_unbonding_balance {:?}",
4113						pool_id,
4114						_pool,
4115						total_balance,
4116						bonded_balance,
4117						sum_unbonding_balance
4118					)
4119			};
4120		}
4121
4122		// Warn if any pool has incorrect ED frozen. We don't want to fail hard as this could be a
4123		// result of an intentional ED change.
4124		let _needs_adjust = Self::check_ed_imbalance()?;
4125
4126		Ok(())
4127	}
4128
4129	/// Check if any pool have an incorrect amount of ED frozen.
4130	///
4131	/// This can happen if the ED has changed since the pool was created.
4132	#[cfg(any(
4133		feature = "try-runtime",
4134		feature = "runtime-benchmarks",
4135		feature = "fuzzing",
4136		test,
4137		debug_assertions
4138	))]
4139	pub fn check_ed_imbalance() -> Result<u32, DispatchError> {
4140		let mut needs_adjust: u32 = 0;
4141		let mut total_pools: u32 = 0;
4142		let mut ed_examples: Vec<PoolId> = Vec::new();
4143		const MAX_EXAMPLES: usize = 10;
4144
4145		BondedPools::<T>::iter_keys().for_each(|id| {
4146			total_pools += 1;
4147			let reward_acc = Self::generate_reward_account(id);
4148			let frozen_balance =
4149				T::Currency::balance_frozen(&FreezeReason::PoolMinBalance.into(), &reward_acc);
4150
4151			let expected_frozen_balance = T::Currency::minimum_balance();
4152			if frozen_balance != expected_frozen_balance {
4153				needs_adjust += 1;
4154				if ed_examples.len() < MAX_EXAMPLES {
4155					ed_examples.push(id);
4156				}
4157				log!(
4158					trace,
4159					"pool {:?} has incorrect ED frozen that can result from change in ED. Expected  = {:?},  Actual = {:?}. Use `adjust_pool_deposit` to fix it",
4160					id,
4161					expected_frozen_balance,
4162					frozen_balance,
4163				);
4164			}
4165		});
4166
4167		if needs_adjust > 0 {
4168			log!(
4169				warn,
4170				"{}/{} pools have incorrect ED frozen (expected {:?}). Use `adjust_pool_deposit` to fix. Examples: {:?}",
4171				needs_adjust,
4172				total_pools,
4173				T::Currency::minimum_balance(),
4174				ed_examples,
4175			);
4176		}
4177
4178		Ok(needs_adjust)
4179	}
4180	/// Fully unbond the shares of `member`, when executed from `origin`.
4181	///
4182	/// This is useful for backwards compatibility with the majority of tests that only deal with
4183	/// full unbonding, not partial unbonding.
4184	#[cfg(any(feature = "runtime-benchmarks", test))]
4185	pub fn fully_unbond(
4186		origin: frame_system::pallet_prelude::OriginFor<T>,
4187		member: T::AccountId,
4188	) -> DispatchResult {
4189		let points = PoolMembers::<T>::get(&member).map(|d| d.active_points()).unwrap_or_default();
4190		let member_lookup = T::Lookup::unlookup(member);
4191		Self::unbond(origin, member_lookup, points)
4192	}
4193}
4194
4195impl<T: Config> Pallet<T> {
4196	/// Returns the pending rewards for the specified `who` account.
4197	///
4198	/// In the case of error, `None` is returned. Used by runtime API.
4199	pub fn api_pending_rewards(who: T::AccountId) -> Option<BalanceOf<T>> {
4200		if let Some(pool_member) = PoolMembers::<T>::get(who) {
4201			if let Some((reward_pool, bonded_pool)) = RewardPools::<T>::get(pool_member.pool_id)
4202				.zip(BondedPools::<T>::get(pool_member.pool_id))
4203			{
4204				let commission = bonded_pool.commission.current();
4205				let (current_reward_counter, _) = reward_pool
4206					.current_reward_counter(pool_member.pool_id, bonded_pool.points, commission)
4207					.ok()?;
4208				return pool_member.pending_rewards(current_reward_counter).ok();
4209			}
4210		}
4211
4212		None
4213	}
4214
4215	/// Returns the points to balance conversion for a specified pool.
4216	///
4217	/// If the pool ID does not exist, it returns 0 ratio points to balance. Used by runtime API.
4218	pub fn api_points_to_balance(pool_id: PoolId, points: BalanceOf<T>) -> BalanceOf<T> {
4219		if let Some(pool) = BondedPool::<T>::get(pool_id) {
4220			pool.points_to_balance(points)
4221		} else {
4222			Zero::zero()
4223		}
4224	}
4225
4226	/// Returns the equivalent `new_funds` balance to point conversion for a specified pool.
4227	///
4228	/// If the pool ID does not exist, returns 0 ratio balance to points. Used by runtime API.
4229	pub fn api_balance_to_points(pool_id: PoolId, new_funds: BalanceOf<T>) -> BalanceOf<T> {
4230		if let Some(pool) = BondedPool::<T>::get(pool_id) {
4231			let bonded_balance =
4232				T::StakeAdapter::active_stake(Pool::from(Self::generate_bonded_account(pool_id)));
4233			Pallet::<T>::balance_to_point(bonded_balance, pool.points, new_funds)
4234		} else {
4235			Zero::zero()
4236		}
4237	}
4238
4239	/// Returns the unapplied slash of the pool.
4240	///
4241	/// Pending slash is only applicable with [`adapter::DelegateStake`] strategy.
4242	pub fn api_pool_pending_slash(pool_id: PoolId) -> BalanceOf<T> {
4243		T::StakeAdapter::pending_slash(Pool::from(Self::generate_bonded_account(pool_id)))
4244	}
4245
4246	/// Returns the unapplied slash of a member.
4247	///
4248	/// Pending slash is only applicable with [`adapter::DelegateStake`] strategy.
4249	///
4250	/// If pending slash of the member exceeds `ExistentialDeposit`, it can be reported on
4251	/// chain via [`Call::apply_slash`].
4252	pub fn api_member_pending_slash(who: T::AccountId) -> BalanceOf<T> {
4253		PoolMembers::<T>::get(who.clone())
4254			.map(|pool_member| {
4255				Self::member_pending_slash(Member::from(who), pool_member).unwrap_or_default()
4256			})
4257			.unwrap_or_default()
4258	}
4259
4260	/// Checks whether pool needs to be migrated to [`adapter::StakeStrategyType::Delegate`]. Only
4261	/// applicable when the [`Config::StakeAdapter`] is [`adapter::DelegateStake`].
4262	///
4263	/// Useful to check this before calling [`Call::migrate_pool_to_delegate_stake`].
4264	pub fn api_pool_needs_delegate_migration(pool_id: PoolId) -> bool {
4265		// if the `Delegate` strategy is not used in the pallet, then no migration required.
4266		if T::StakeAdapter::strategy_type() != adapter::StakeStrategyType::Delegate {
4267			return false;
4268		}
4269
4270		// if pool does not exist, return false.
4271		if !BondedPools::<T>::contains_key(pool_id) {
4272			return false;
4273		}
4274
4275		let pool_account = Self::generate_bonded_account(pool_id);
4276
4277		// true if pool is still not migrated to `DelegateStake`.
4278		T::StakeAdapter::pool_strategy(Pool::from(pool_account)) !=
4279			adapter::StakeStrategyType::Delegate
4280	}
4281
4282	/// Checks whether member delegation needs to be migrated to
4283	/// [`adapter::StakeStrategyType::Delegate`]. Only applicable when the [`Config::StakeAdapter`]
4284	/// is [`adapter::DelegateStake`].
4285	///
4286	/// Useful to check this before calling [`Call::migrate_delegation`].
4287	pub fn api_member_needs_delegate_migration(who: T::AccountId) -> bool {
4288		// if the `Delegate` strategy is not used in the pallet, then no migration required.
4289		if T::StakeAdapter::strategy_type() != adapter::StakeStrategyType::Delegate {
4290			return false;
4291		}
4292
4293		PoolMembers::<T>::get(who.clone())
4294			.map(|pool_member| {
4295				if Self::api_pool_needs_delegate_migration(pool_member.pool_id) {
4296					// the pool needs to be migrated before members can be migrated.
4297					return false;
4298				}
4299
4300				let member_balance = pool_member.total_balance();
4301				let delegated_balance =
4302					T::StakeAdapter::member_delegation_balance(Member::from(who.clone()));
4303
4304				// if the member has no delegation but has some balance in the pool, then it needs
4305				// to be migrated.
4306				delegated_balance.is_none() && !member_balance.is_zero()
4307			})
4308			.unwrap_or_default()
4309	}
4310
4311	/// Contribution of the member in the pool.
4312	///
4313	/// Includes balance that is unbonded from staking but not claimed yet from the pool, therefore
4314	/// this balance can be higher than the staked funds.
4315	pub fn api_member_total_balance(who: T::AccountId) -> BalanceOf<T> {
4316		PoolMembers::<T>::get(who.clone())
4317			.map(|m| m.total_balance())
4318			.unwrap_or_default()
4319	}
4320
4321	/// Total balance contributed to the pool.
4322	pub fn api_pool_balance(pool_id: PoolId) -> BalanceOf<T> {
4323		T::StakeAdapter::total_balance(Pool::from(Self::generate_bonded_account(pool_id)))
4324			.unwrap_or_default()
4325	}
4326
4327	/// Returns the bonded account and reward account associated with the pool_id.
4328	pub fn api_pool_accounts(pool_id: PoolId) -> (T::AccountId, T::AccountId) {
4329		let bonded_account = Self::generate_bonded_account(pool_id);
4330		let reward_account = Self::generate_reward_account(pool_id);
4331		(bonded_account, reward_account)
4332	}
4333}
4334
4335impl<T: Config> sp_staking::OnStakingUpdate<T::AccountId, BalanceOf<T>> for Pallet<T> {
4336	/// Reduces the balances of the [`SubPools`], that belong to the pool involved in the
4337	/// slash, to the amount that is defined in the `slashed_unlocking` field of
4338	/// [`sp_staking::OnStakingUpdate::on_slash`]
4339	///
4340	/// Emits the `PoolsSlashed` event.
4341	fn on_slash(
4342		pool_account: &T::AccountId,
4343		// Bonded balance is always read directly from staking, therefore we don't need to update
4344		// anything here.
4345		slashed_bonded: BalanceOf<T>,
4346		slashed_unlocking: &BTreeMap<EraIndex, BalanceOf<T>>,
4347		total_slashed: BalanceOf<T>,
4348	) {
4349		let Some(pool_id) = ReversePoolIdLookup::<T>::get(pool_account) else { return };
4350		// As the slashed account belongs to a `BondedPool` the `TotalValueLocked` decreases and
4351		// an event is emitted.
4352		TotalValueLocked::<T>::mutate(|tvl| {
4353			tvl.defensive_saturating_reduce(total_slashed);
4354		});
4355
4356		if let Some(mut sub_pools) = SubPoolsStorage::<T>::get(pool_id) {
4357			// set the reduced balance for each of the `SubPools`
4358			slashed_unlocking.iter().for_each(|(era, slashed_balance)| {
4359				if let Some(pool) = sub_pools.with_era.get_mut(era).defensive() {
4360					pool.balance = *slashed_balance;
4361					Self::deposit_event(Event::<T>::UnbondingPoolSlashed {
4362						era: *era,
4363						pool_id,
4364						balance: *slashed_balance,
4365					});
4366				}
4367			});
4368			SubPoolsStorage::<T>::insert(pool_id, sub_pools);
4369		} else if !slashed_unlocking.is_empty() {
4370			defensive!("Expected SubPools were not found");
4371		}
4372		Self::deposit_event(Event::<T>::PoolSlashed { pool_id, balance: slashed_bonded });
4373	}
4374
4375	/// Reduces the overall `TotalValueLocked` if a withdrawal happened for a pool involved in the
4376	/// staking withdraw.
4377	fn on_withdraw(pool_account: &T::AccountId, amount: BalanceOf<T>) {
4378		if ReversePoolIdLookup::<T>::get(pool_account).is_some() {
4379			TotalValueLocked::<T>::mutate(|tvl| {
4380				tvl.saturating_reduce(amount);
4381			});
4382		}
4383	}
4384}
4385
4386/// A utility struct that provides a way to check if a given account is a pool member.
4387pub struct AllPoolMembers<T: Config>(PhantomData<T>);
4388impl<T: Config> Contains<T::AccountId> for AllPoolMembers<T> {
4389	fn contains(t: &T::AccountId) -> bool {
4390		PoolMembers::<T>::contains_key(t)
4391	}
4392}