diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index cd223c319ff3..fc7a7aa66c17 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1159,8 +1159,38 @@ impl Pallet { } pub(super) fn do_migrate_currency(stash: &T::AccountId) -> DispatchResult { - // we can't do anything for virtual stakers since their funds are not managed/held by - // this pallet. + if Self::is_virtual_staker(stash) { + // Funds for virtual stakers not managed/held by this pallet. We only need to clear + // the extra consumer we used to have with OldCurrency. + frame_system::Pallet::::dec_consumers(&stash); + + // The delegation system that manages the virtual staker needed to increment provider + // previously because of the consumer needed by this pallet. In reality, this stash + // is just a key for managing the ledger and the account does not need to hold any + // balance or exist. We decrement this provider. + let actual_providers = frame_system::Pallet::::providers(stash); + + let expected_providers = + // provider is expected to be 1 but someone can always transfer some free funds to + // these accounts, increasing the provider. + if asset::free_to_stake::(&stash) >= asset::existential_deposit::() { + 2 + } else { + 1 + }; + + // We should never have more than expected providers. + ensure!(actual_providers <= expected_providers, Error::::BadState); + + // if actual provider is less than expected, it is already migrated. + ensure!(actual_providers == expected_providers, Error::::AlreadyMigrated); + + // dec provider + let _ = frame_system::Pallet::::dec_providers(&stash)?; + + return Ok(()) + } + ensure!(!Self::is_virtual_staker(stash), Error::::VirtualStakerNotAllowed); let ledger = Self::ledger(Stash(stash.clone()))?; let locked: BalanceOf = T::OldCurrency::balance_locked(STAKING_ID, stash).into();