Skip to main content

pallet_nomination_pools/
lib.rs

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