Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions substrate/frame/nomination-pools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3955,12 +3955,18 @@ impl<T: Config> Pallet<T> {
);

let depositor = PoolMembers::<T>::get(&bonded_pool.roles.depositor).unwrap();
ensure!(
bonded_pool.is_destroying_and_only_depositor(depositor.active_points()) ||
depositor.active_points() >= MinCreateBond::<T>::get(),
"depositor must always have MinCreateBond stake in the pool, except for when the \
pool is being destroyed and the depositor is the last member",
);
let depositor_has_enough_stake = bonded_pool
.is_destroying_and_only_depositor(depositor.active_points()) ||
depositor.active_points() >= MinCreateBond::<T>::get();
if !depositor_has_enough_stake {
log::warn!(
"pool {:?} has depositor {:?} with insufficient stake {:?}, minimum required is {:?}",
id,
bonded_pool.roles.depositor,
depositor.active_points(),
MinCreateBond::<T>::get()
);
}

ensure!(
bonded_pool.points >= bonded_pool.points_to_balance(bonded_pool.points),
Expand Down
47 changes: 29 additions & 18 deletions substrate/frame/staking-async/src/pallet/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,26 +73,18 @@ use sp_runtime::TryRuntimeError;
const NPOS_MAX_ITERATIONS_COEFFICIENT: u32 = 2;

impl<T: Config> Pallet<T> {
/// Returns the minimum required bond for participation, considering nominators,
/// and the chain’s existential deposit.
///
/// This function computes the smallest allowed bond among `MinValidatorBond` and
/// `MinNominatorBond`, but ensures it is not below the existential deposit required to keep an
/// account alive.
/// Returns the minimum required bond for participation in staking as a chilled account.
pub(crate) fn min_chilled_bond() -> BalanceOf<T> {
MinValidatorBond::<T>::get()
.min(MinNominatorBond::<T>::get())
.max(asset::existential_deposit::<T>())
// Note: in the future we might add a configurable `MinChilledBond`, else ED is good.
Comment thread
kianenigma marked this conversation as resolved.
Outdated
asset::existential_deposit::<T>()
}

/// Returns the minimum required bond for validators, defaulting to `MinNominatorBond` if not
/// set or less than `MinNominatorBond`.
/// Returns the minimum required bond for participation in staking as a validator account.
pub(crate) fn min_validator_bond() -> BalanceOf<T> {
MinValidatorBond::<T>::get().max(Self::min_nominator_bond())
MinValidatorBond::<T>::get().max(asset::existential_deposit::<T>())
}

/// Returns the minimum required bond for nominators, considering the chain’s existential
/// deposit.
/// Returns the minimum required bond for participation in staking as a nominator account.
pub(crate) fn min_nominator_bond() -> BalanceOf<T> {
MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>())
}
Expand Down Expand Up @@ -2086,21 +2078,40 @@ impl<T: Config> Pallet<T> {

match (is_nominator, is_validator) {
(false, false) => {
if ledger.active < Self::min_chilled_bond() {
log!(warn, "Chilled stash {:?} has less than minimum bond", stash);
if ledger.active < Self::min_chilled_bond() || ledger.active.is_zero() {
Comment thread
kianenigma marked this conversation as resolved.
Outdated
// chilled accounts allow to go to zero and fully unbond ^^^^^^^^^
log!(
warn,
"Chilled stash {:?} has less stake ({:?}) than minimum role bond ({:?})",
stash,
ledger.active,
Self::min_chilled_bond()
);
}
// is chilled
},
(true, false) => {
// Nominators must have a minimum bond.
if ledger.active < Self::min_nominator_bond() {
log!(warn, "Nominator {:?} has less than minimum bond", stash);
log!(
warn,
"Nominator {:?} has less stake ({:?}) than minimum role bond ({:?})",
stash,
ledger.active,
Self::min_nominator_bond()
);
}
},
(false, true) => {
// Validators must have a minimum bond.
if ledger.active < Self::min_validator_bond() {
log!(warn, "Validator {:?} has less than minimum bond", stash);
log!(
warn,
"Validator {:?} has less stake ({:?}) than minimum role bond ({:?})",
stash,
ledger.active,
Self::min_validator_bond()
);
}
},
(true, true) => {
Expand Down
10 changes: 4 additions & 6 deletions substrate/frame/staking-async/src/tests/bonding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,6 @@ mod reap {
Error::<Test>::FundedTarget
);

// Note: Even though the stash is a validator, the threshold to reap is min of
// nominator and validator bond
// no easy way to cause an account to go below ED, we tweak their staking ledger
// instead.

Expand All @@ -1401,8 +1399,8 @@ mod reap {
Error::<Test>::FundedTarget
);

// WHEN: set ledger to below min nominator bond.
Ledger::<Test>::insert(11, StakingLedger::<Test>::new(11, 999));
// WHEN: set ledger to below ED
Ledger::<Test>::insert(11, StakingLedger::<Test>::new(11, 9));

// THEN: reap-able
assert_ok!(Staking::reap_stash(RuntimeOrigin::signed(20), 11, 0));
Expand Down Expand Up @@ -1551,9 +1549,9 @@ mod staking_bounds_chill_other {
.min_nominator_bond(1_000)
.min_validator_bond(1_500)
.build_and_execute(|| {
// 500 is not enough for any role
// 50 is not enough for any role (less than ED)
assert_noop!(
Staking::bond(RuntimeOrigin::signed(3), 500, RewardDestination::Stash),
Staking::bond(RuntimeOrigin::signed(3), 50, RewardDestination::Stash),
Error::<Test>::InsufficientBond
);
// 1000 is enough for nominator but not for validator.
Expand Down
Loading