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