5a184fd7dc
## Changes ### High Impact Fixes (RED) - Fix radium git URL (https://https:// → github.com/paritytech/radium-0.7-fork) - Fix rustc-rv32e-toolchain URL (nickvidal → paritytech) - Fix chainextension-registry URL (nickvidal/substrate-contracts-node → paritytech/chainextension-registry) ### Medium Impact Fixes (YELLOW) - Fix docs.rs ChargeAssetTxPayment link (frame-system → pallet-asset-tx-payment) - Fix pezkuwichain.github.io → paritytech.github.io for: - json-rpc-interface-spec - substrate docs - try-runtime-cli - Fix subxt issue reference (pezkuwichain → paritytech) ### Zero Impact Excludes (GREEN) - Add 40+ defunct chain websites to lychee exclude list - Add commit-specific GitHub URLs to exclude (cannot migrate) - Add rate-limited/403 sites to exclude ### Documentation - Refactor .claude/domains_repositories.md structure - Add tracking issue mapping and creation scripts - Update external repo links to use original URLs Result: 🔍 9610 Total ✅ 6747 OK 🚫 0 Errors
1163 lines
36 KiB
Rust
1163 lines
36 KiB
Rust
// This file is part of Bizinikiwi.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use super::*;
|
|
use alloc::{collections::btree_map::BTreeMap, vec::Vec};
|
|
use pezframe_support::traits::{OnRuntimeUpgrade, UncheckedOnRuntimeUpgrade};
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
use pezsp_runtime::TryRuntimeError;
|
|
|
|
/// Exports for versioned migration `type`s for this pezpallet.
|
|
pub mod versioned {
|
|
use super::*;
|
|
|
|
/// v8: Adds commission claim permissions to `BondedPools`.
|
|
pub type V7ToV8<T> = pezframe_support::migrations::VersionedMigration<
|
|
7,
|
|
8,
|
|
v8::VersionUncheckedMigrateV7ToV8<T>,
|
|
crate::pezpallet::Pezpallet<T>,
|
|
<T as pezframe_system::Config>::DbWeight,
|
|
>;
|
|
|
|
/// Migration V6 to V7 wrapped in a [`pezframe_support::migrations::VersionedMigration`],
|
|
/// ensuring the migration is only performed when on-chain version is 6.
|
|
pub type V6ToV7<T> = pezframe_support::migrations::VersionedMigration<
|
|
6,
|
|
7,
|
|
v7::VersionUncheckedMigrateV6ToV7<T>,
|
|
crate::pezpallet::Pezpallet<T>,
|
|
<T as pezframe_system::Config>::DbWeight,
|
|
>;
|
|
|
|
/// Wrapper over `MigrateToV6` with convenience version checks.
|
|
pub type V5toV6<T> = pezframe_support::migrations::VersionedMigration<
|
|
5,
|
|
6,
|
|
v6::MigrateToV6<T>,
|
|
crate::pezpallet::Pezpallet<T>,
|
|
<T as pezframe_system::Config>::DbWeight,
|
|
>;
|
|
}
|
|
|
|
pub mod unversioned {
|
|
use super::*;
|
|
|
|
/// Checks and updates `TotalValueLocked` if out of sync.
|
|
pub struct TotalValueLockedSync<T>(core::marker::PhantomData<T>);
|
|
impl<T: Config> OnRuntimeUpgrade for TotalValueLockedSync<T> {
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
Ok(Vec::new())
|
|
}
|
|
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let migrated = BondedPools::<T>::count();
|
|
|
|
// recalculate the `TotalValueLocked` to compare with the current on-chain TVL which may
|
|
// be out of sync.
|
|
let tvl: BalanceOf<T> = helpers::calculate_tvl_by_total_stake::<T>();
|
|
let onchain_tvl = TotalValueLocked::<T>::get();
|
|
|
|
let writes = if tvl != onchain_tvl {
|
|
TotalValueLocked::<T>::set(tvl);
|
|
|
|
log!(
|
|
info,
|
|
"on-chain TVL was out of sync, update. Old: {:?}, new: {:?}",
|
|
onchain_tvl,
|
|
tvl
|
|
);
|
|
|
|
// writes: onchain version + set total value locked.
|
|
2
|
|
} else {
|
|
log!(info, "on-chain TVL was OK: {:?}", tvl);
|
|
|
|
// writes: onchain version write.
|
|
1
|
|
};
|
|
|
|
// reads: migrated * (BondedPools + Staking::total_stake) + count + onchain
|
|
// version
|
|
//
|
|
// writes: current version + (maybe) TVL
|
|
T::DbWeight::get()
|
|
.reads_writes(migrated.saturating_mul(2).saturating_add(2).into(), writes)
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Migrate existing pools from [`adapter::StakeStrategyType::Transfer`] to
|
|
/// [`adapter::StakeStrategyType::Delegate`].
|
|
///
|
|
/// Note: This only migrates the pools, the members are not migrated. They can use the
|
|
/// permission-less [`Pezpallet::migrate_delegation()`] to migrate their funds.
|
|
///
|
|
/// This migration does not break any existing pool storage item, does not need to happen in any
|
|
/// sequence and hence can be applied unversioned on a production runtime.
|
|
///
|
|
/// Takes `MaxPools` as type parameter to limit the number of pools that should be migrated in a
|
|
/// single block. It should be set such that migration weight does not exceed the block weight
|
|
/// limit. If all pools can be safely migrated, it is good to keep this number a little higher
|
|
/// than the actual number of pools to handle any extra pools created while the migration is
|
|
/// proposed, and before it is executed.
|
|
///
|
|
/// If there are pools that fail to migrate or did not fit in the bounds, the remaining pools
|
|
/// can be migrated via the permission-less extrinsic [`Call::migrate_pool_to_delegate_stake`].
|
|
pub struct DelegationStakeMigration<T, MaxPools>(core::marker::PhantomData<(T, MaxPools)>);
|
|
|
|
impl<T: Config, MaxPools: Get<u32>> OnRuntimeUpgrade for DelegationStakeMigration<T, MaxPools> {
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let mut count: u32 = 0;
|
|
|
|
BondedPools::<T>::iter_keys().take(MaxPools::get() as usize).for_each(|id| {
|
|
let pool_acc = Pezpallet::<T>::generate_bonded_account(id);
|
|
|
|
// only migrate if the pool is in Transfer Strategy.
|
|
if T::StakeAdapter::pool_strategy(Pool::from(pool_acc))
|
|
== adapter::StakeStrategyType::Transfer
|
|
{
|
|
let _ = Pezpallet::<T>::migrate_to_delegate_stake(id).map_err(|err| {
|
|
log!(
|
|
warn,
|
|
"failed to migrate pool {:?} to delegate stake strategy with err: {:?}",
|
|
id,
|
|
err
|
|
)
|
|
});
|
|
count.saturating_inc();
|
|
}
|
|
});
|
|
|
|
log!(info, "migrated {:?} pools to delegate stake strategy", count);
|
|
|
|
// reads: (bonded pool key + current pool strategy) * MaxPools (worst case)
|
|
T::DbWeight::get()
|
|
.reads_writes(2, 0)
|
|
.saturating_mul(MaxPools::get() as u64)
|
|
// migration weight: `pool_migrate` weight * count
|
|
.saturating_add(T::WeightInfo::pool_migrate().saturating_mul(count.into()))
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
// ensure stake adapter is correct.
|
|
ensure!(
|
|
T::StakeAdapter::strategy_type() == adapter::StakeStrategyType::Delegate,
|
|
"Current strategy is not `Delegate"
|
|
);
|
|
|
|
if BondedPools::<T>::count() > MaxPools::get() {
|
|
// we log a warning if the number of pools exceeds the bound.
|
|
log!(
|
|
warn,
|
|
"Number of pools {} exceeds the maximum bound {}. This would leave some pools unmigrated.", BondedPools::<T>::count(), MaxPools::get()
|
|
);
|
|
}
|
|
|
|
let mut pool_balances: Vec<BalanceOf<T>> = Vec::new();
|
|
BondedPools::<T>::iter_keys().take(MaxPools::get() as usize).for_each(|id| {
|
|
let pool_account = Pezpallet::<T>::generate_bonded_account(id);
|
|
|
|
// we ensure migration is idempotent.
|
|
let pool_balance = T::StakeAdapter::total_balance(Pool::from(pool_account.clone()))
|
|
// we check actual account balance if pool has not migrated yet.
|
|
.unwrap_or(T::Currency::total_balance(&pool_account));
|
|
|
|
pool_balances.push(pool_balance);
|
|
});
|
|
|
|
Ok(pool_balances.encode())
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(data: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
let expected_pool_balances: Vec<BalanceOf<T>> = Decode::decode(&mut &data[..]).unwrap();
|
|
|
|
for (index, id) in
|
|
BondedPools::<T>::iter_keys().take(MaxPools::get() as usize).enumerate()
|
|
{
|
|
let pool_account = Pezpallet::<T>::generate_bonded_account(id);
|
|
if T::StakeAdapter::pool_strategy(Pool::from(pool_account.clone()))
|
|
== adapter::StakeStrategyType::Transfer
|
|
{
|
|
log!(error, "Pool {} failed to migrate", id,);
|
|
return Err(TryRuntimeError::Other("Pool failed to migrate"));
|
|
}
|
|
|
|
let actual_balance =
|
|
T::StakeAdapter::total_balance(Pool::from(pool_account.clone()))
|
|
.expect("after migration, this should return a value");
|
|
let expected_balance = expected_pool_balances.get(index).unwrap();
|
|
|
|
if actual_balance != *expected_balance {
|
|
log!(
|
|
error,
|
|
"Pool {} balance mismatch. Expected: {:?}, Actual: {:?}",
|
|
id,
|
|
expected_balance,
|
|
actual_balance
|
|
);
|
|
return Err(TryRuntimeError::Other("Pool balance mismatch"));
|
|
}
|
|
|
|
// account balance should be zero.
|
|
let pool_account_balance = T::Currency::total_balance(&pool_account);
|
|
if pool_account_balance != Zero::zero() {
|
|
log!(
|
|
error,
|
|
"Pool account balance was expected to be zero. Pool: {}, Balance: {:?}",
|
|
id,
|
|
pool_account_balance
|
|
);
|
|
return Err(TryRuntimeError::Other("Pool account balance not migrated"));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub mod v8 {
|
|
use super::{v7::V7BondedPoolInner, *};
|
|
|
|
impl<T: Config> V7BondedPoolInner<T> {
|
|
fn migrate_to_v8(self) -> BondedPoolInner<T> {
|
|
BondedPoolInner {
|
|
commission: Commission {
|
|
current: self.commission.current,
|
|
max: self.commission.max,
|
|
change_rate: self.commission.change_rate,
|
|
throttle_from: self.commission.throttle_from,
|
|
// `claim_permission` is a new field.
|
|
claim_permission: None,
|
|
},
|
|
member_counter: self.member_counter,
|
|
points: self.points,
|
|
roles: self.roles,
|
|
state: self.state,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct VersionUncheckedMigrateV7ToV8<T>(core::marker::PhantomData<T>);
|
|
impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV7ToV8<T> {
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
Ok(Vec::new())
|
|
}
|
|
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let mut translated = 0u64;
|
|
BondedPools::<T>::translate::<V7BondedPoolInner<T>, _>(|_key, old_value| {
|
|
translated.saturating_inc();
|
|
Some(old_value.migrate_to_v8())
|
|
});
|
|
T::DbWeight::get().reads_writes(translated, translated + 1)
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
// Check new `claim_permission` field is present.
|
|
ensure!(
|
|
BondedPools::<T>::iter()
|
|
.all(|(_, inner)| inner.commission.claim_permission.is_none()),
|
|
"`claim_permission` value has not been set correctly."
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This migration accumulates and initializes the [`TotalValueLocked`] for all pools.
|
|
///
|
|
/// WARNING: This migration works under the assumption that the [`BondedPools`] cannot be inflated
|
|
/// arbitrarily. Otherwise this migration could fail due to too high weight.
|
|
pub(crate) mod v7 {
|
|
use super::*;
|
|
|
|
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)]
|
|
#[codec(mel_bound(T: Config))]
|
|
#[scale_info(skip_type_params(T))]
|
|
pub struct V7Commission<T: Config> {
|
|
pub current: Option<(Perbill, T::AccountId)>,
|
|
pub max: Option<Perbill>,
|
|
pub change_rate: Option<CommissionChangeRate<BlockNumberFor<T>>>,
|
|
pub throttle_from: Option<BlockNumberFor<T>>,
|
|
}
|
|
|
|
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)]
|
|
#[codec(mel_bound(T: Config))]
|
|
#[scale_info(skip_type_params(T))]
|
|
pub struct V7BondedPoolInner<T: Config> {
|
|
pub commission: V7Commission<T>,
|
|
pub member_counter: u32,
|
|
pub points: BalanceOf<T>,
|
|
pub roles: PoolRoles<T::AccountId>,
|
|
pub state: PoolState,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(RuntimeDebugNoBound)]
|
|
#[cfg_attr(feature = "std", derive(Clone, PartialEq))]
|
|
pub struct V7BondedPool<T: Config> {
|
|
/// The identifier of the pool.
|
|
id: PoolId,
|
|
/// The inner fields.
|
|
inner: V7BondedPoolInner<T>,
|
|
}
|
|
|
|
impl<T: Config> V7BondedPool<T> {
|
|
#[allow(dead_code)]
|
|
fn bonded_account(&self) -> T::AccountId {
|
|
Pezpallet::<T>::generate_bonded_account(self.id)
|
|
}
|
|
}
|
|
|
|
// NOTE: We cannot put a V7 prefix here since that would change the storage key.
|
|
#[pezframe_support::storage_alias]
|
|
pub type BondedPools<T: Config> =
|
|
CountedStorageMap<Pezpallet<T>, Twox64Concat, PoolId, V7BondedPoolInner<T>>;
|
|
|
|
pub struct VersionUncheckedMigrateV6ToV7<T>(core::marker::PhantomData<T>);
|
|
impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV6ToV7<T> {
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let migrated = BondedPools::<T>::count();
|
|
// The TVL should be the sum of all the funds that are actively staked and in the
|
|
// unbonding process of the account of each pool.
|
|
let tvl: BalanceOf<T> = helpers::calculate_tvl_by_total_stake::<T>();
|
|
|
|
TotalValueLocked::<T>::set(tvl);
|
|
|
|
log!(info, "Upgraded {} pools with a TVL of {:?}", migrated, tvl);
|
|
|
|
// reads: migrated * (BondedPools + Staking::total_stake) + count + onchain
|
|
// version
|
|
//
|
|
// writes: current version + TVL
|
|
T::DbWeight::get().reads_writes(migrated.saturating_mul(2).saturating_add(2).into(), 2)
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
Ok(Vec::new())
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(_data: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
// check that the `TotalValueLocked` written is actually the sum of `total_stake` of the
|
|
// `BondedPools``
|
|
let tvl: BalanceOf<T> = helpers::calculate_tvl_by_total_stake::<T>();
|
|
ensure!(
|
|
TotalValueLocked::<T>::get() == tvl,
|
|
"TVL written is not equal to `Staking::total_stake` of all `BondedPools`."
|
|
);
|
|
|
|
// calculate the sum of `total_balance` of all `PoolMember` as the upper bound for the
|
|
// `TotalValueLocked`.
|
|
let total_balance_members: BalanceOf<T> = PoolMembers::<T>::iter()
|
|
.map(|(_, member)| member.total_balance())
|
|
.reduce(|acc, total_balance| acc + total_balance)
|
|
.unwrap_or_default();
|
|
|
|
ensure!(
|
|
TotalValueLocked::<T>::get() <= total_balance_members,
|
|
"TVL is greater than the balance of all PoolMembers."
|
|
);
|
|
|
|
ensure!(
|
|
Pezpallet::<T>::on_chain_storage_version() >= 7,
|
|
"nomination-pools::migration::v7: wrong storage version"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
mod v6 {
|
|
use super::*;
|
|
|
|
/// This migration would restrict reward account of pools to go below ED by doing a named
|
|
/// freeze on all the existing pools.
|
|
pub struct MigrateToV6<T>(core::marker::PhantomData<T>);
|
|
|
|
impl<T: Config> MigrateToV6<T> {
|
|
fn freeze_ed(pool_id: PoolId) -> Result<(), ()> {
|
|
let reward_acc = Pezpallet::<T>::generate_reward_account(pool_id);
|
|
Pezpallet::<T>::freeze_pool_deposit(&reward_acc).map_err(|e| {
|
|
log!(error, "Failed to freeze ED for pool {} with error: {:?}", pool_id, e);
|
|
()
|
|
})
|
|
}
|
|
}
|
|
impl<T: Config> UncheckedOnRuntimeUpgrade for MigrateToV6<T> {
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let mut success = 0u64;
|
|
let mut fail = 0u64;
|
|
|
|
BondedPools::<T>::iter_keys().for_each(|p| {
|
|
if Self::freeze_ed(p).is_ok() {
|
|
success.saturating_inc();
|
|
} else {
|
|
fail.saturating_inc();
|
|
}
|
|
});
|
|
|
|
if fail > 0 {
|
|
log!(error, "Failed to freeze ED for {} pools", fail);
|
|
} else {
|
|
log!(info, "Freezing ED succeeded for {} pools", success);
|
|
}
|
|
|
|
let total = success.saturating_add(fail);
|
|
// freeze_ed = r:2 w:2
|
|
// reads: (freeze_ed + bonded pool key) * total
|
|
// writes: freeze_ed * total
|
|
T::DbWeight::get().reads_writes(3u64.saturating_mul(total), 2u64.saturating_mul(total))
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(_data: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
// there should be no ED imbalances anymore..
|
|
Pezpallet::<T>::check_ed_imbalance().map(|_| ())
|
|
}
|
|
}
|
|
}
|
|
pub mod v5 {
|
|
use super::*;
|
|
|
|
#[derive(Decode)]
|
|
pub struct OldRewardPool<T: Config> {
|
|
last_recorded_reward_counter: T::RewardCounter,
|
|
last_recorded_total_payouts: BalanceOf<T>,
|
|
total_rewards_claimed: BalanceOf<T>,
|
|
}
|
|
|
|
impl<T: Config> OldRewardPool<T> {
|
|
fn migrate_to_v5(self) -> RewardPool<T> {
|
|
RewardPool {
|
|
last_recorded_reward_counter: self.last_recorded_reward_counter,
|
|
last_recorded_total_payouts: self.last_recorded_total_payouts,
|
|
total_rewards_claimed: self.total_rewards_claimed,
|
|
total_commission_pending: Zero::zero(),
|
|
total_commission_claimed: Zero::zero(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This migration adds `total_commission_pending` and `total_commission_claimed` field to every
|
|
/// `RewardPool`, if any.
|
|
pub struct MigrateToV5<T>(core::marker::PhantomData<T>);
|
|
impl<T: Config> OnRuntimeUpgrade for MigrateToV5<T> {
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let in_code = Pezpallet::<T>::in_code_storage_version();
|
|
let onchain = Pezpallet::<T>::on_chain_storage_version();
|
|
|
|
log!(
|
|
info,
|
|
"Running migration with in-code storage version {:?} / onchain {:?}",
|
|
in_code,
|
|
onchain
|
|
);
|
|
|
|
if in_code == 5 && onchain == 4 {
|
|
let mut translated = 0u64;
|
|
RewardPools::<T>::translate::<OldRewardPool<T>, _>(|_id, old_value| {
|
|
translated.saturating_inc();
|
|
Some(old_value.migrate_to_v5())
|
|
});
|
|
|
|
in_code.put::<Pezpallet<T>>();
|
|
log!(info, "Upgraded {} pools, storage to version {:?}", translated, in_code);
|
|
|
|
// reads: translated + onchain version.
|
|
// writes: translated + current.put.
|
|
T::DbWeight::get().reads_writes(translated + 1, translated + 1)
|
|
} else {
|
|
log!(info, "Migration did not execute. This probably should be removed");
|
|
T::DbWeight::get().reads(1)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
let rpool_keys = RewardPools::<T>::iter_keys().count();
|
|
let rpool_values = RewardPools::<T>::iter_values().count();
|
|
if rpool_keys != rpool_values {
|
|
log!(info, "🔥 There are {} undecodable RewardPools in storage. This migration will try to correct them. keys: {}, values: {}", rpool_keys.saturating_sub(rpool_values), rpool_keys, rpool_values);
|
|
}
|
|
|
|
ensure!(
|
|
PoolMembers::<T>::iter_keys().count() == PoolMembers::<T>::iter_values().count(),
|
|
"There are undecodable PoolMembers in storage. This migration will not fix that."
|
|
);
|
|
ensure!(
|
|
BondedPools::<T>::iter_keys().count() == BondedPools::<T>::iter_values().count(),
|
|
"There are undecodable BondedPools in storage. This migration will not fix that."
|
|
);
|
|
ensure!(
|
|
SubPoolsStorage::<T>::iter_keys().count()
|
|
== SubPoolsStorage::<T>::iter_values().count(),
|
|
"There are undecodable SubPools in storage. This migration will not fix that."
|
|
);
|
|
ensure!(
|
|
Metadata::<T>::iter_keys().count() == Metadata::<T>::iter_values().count(),
|
|
"There are undecodable Metadata in storage. This migration will not fix that."
|
|
);
|
|
|
|
Ok((rpool_values as u64).encode())
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(data: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
let old_rpool_values: u64 = Decode::decode(&mut &data[..]).unwrap();
|
|
let rpool_keys = RewardPools::<T>::iter_keys().count() as u64;
|
|
let rpool_values = RewardPools::<T>::iter_values().count() as u64;
|
|
ensure!(
|
|
rpool_keys == rpool_values,
|
|
"There are STILL undecodable RewardPools - migration failed"
|
|
);
|
|
|
|
if old_rpool_values != rpool_values {
|
|
log!(
|
|
info,
|
|
"🎉 Fixed {} undecodable RewardPools.",
|
|
rpool_values.saturating_sub(old_rpool_values)
|
|
);
|
|
}
|
|
|
|
// ensure all RewardPools items now contain `total_commission_pending` and
|
|
// `total_commission_claimed` field.
|
|
ensure!(
|
|
RewardPools::<T>::iter().all(|(_, reward_pool)| reward_pool
|
|
.total_commission_pending
|
|
>= Zero::zero() && reward_pool
|
|
.total_commission_claimed
|
|
>= Zero::zero()),
|
|
"a commission value has been incorrectly set"
|
|
);
|
|
ensure!(
|
|
Pezpallet::<T>::on_chain_storage_version() >= 5,
|
|
"nomination-pools::migration::v5: wrong storage version"
|
|
);
|
|
|
|
// These should not have been touched - just in case.
|
|
ensure!(
|
|
PoolMembers::<T>::iter_keys().count() == PoolMembers::<T>::iter_values().count(),
|
|
"There are undecodable PoolMembers in storage."
|
|
);
|
|
ensure!(
|
|
BondedPools::<T>::iter_keys().count() == BondedPools::<T>::iter_values().count(),
|
|
"There are undecodable BondedPools in storage."
|
|
);
|
|
ensure!(
|
|
SubPoolsStorage::<T>::iter_keys().count()
|
|
== SubPoolsStorage::<T>::iter_values().count(),
|
|
"There are undecodable SubPools in storage."
|
|
);
|
|
ensure!(
|
|
Metadata::<T>::iter_keys().count() == Metadata::<T>::iter_values().count(),
|
|
"There are undecodable Metadata in storage."
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub mod v4 {
|
|
use super::*;
|
|
|
|
#[derive(Decode)]
|
|
pub struct OldBondedPoolInner<T: Config> {
|
|
pub points: BalanceOf<T>,
|
|
pub state: PoolState,
|
|
pub member_counter: u32,
|
|
pub roles: PoolRoles<T::AccountId>,
|
|
}
|
|
|
|
impl<T: Config> OldBondedPoolInner<T> {
|
|
fn migrate_to_v4(self) -> BondedPoolInner<T> {
|
|
BondedPoolInner {
|
|
commission: Commission::default(),
|
|
member_counter: self.member_counter,
|
|
points: self.points,
|
|
state: self.state,
|
|
roles: self.roles,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Migrates from `v3` directly to `v5` to avoid the broken `v4` migration.
|
|
#[allow(deprecated)]
|
|
pub type MigrateV3ToV5<T, U> = (v4::MigrateToV4<T, U>, v5::MigrateToV5<T>);
|
|
|
|
/// # Warning
|
|
///
|
|
/// To avoid mangled storage please use `MigrateV3ToV5` instead.
|
|
///
|
|
/// This migration adds a `commission` field to every `BondedPoolInner`, if
|
|
/// any.
|
|
#[deprecated(note = "To avoid mangled storage please use `MigrateV3ToV5` instead.")]
|
|
pub struct MigrateToV4<T, U>(core::marker::PhantomData<(T, U)>);
|
|
#[allow(deprecated)]
|
|
impl<T: Config, U: Get<Perbill>> OnRuntimeUpgrade for MigrateToV4<T, U> {
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let current = Pezpallet::<T>::in_code_storage_version();
|
|
let onchain = Pezpallet::<T>::on_chain_storage_version();
|
|
|
|
log!(
|
|
info,
|
|
"Running migration with in-code storage version {:?} / onchain {:?}",
|
|
current,
|
|
onchain
|
|
);
|
|
|
|
if onchain == 3 {
|
|
log!(warn, "Please run MigrateToV5 immediately after this migration.");
|
|
let initial_global_max_commission = U::get();
|
|
GlobalMaxCommission::<T>::set(Some(initial_global_max_commission));
|
|
log!(
|
|
info,
|
|
"Set initial global max commission to {:?}.",
|
|
initial_global_max_commission
|
|
);
|
|
|
|
let mut translated = 0u64;
|
|
BondedPools::<T>::translate::<OldBondedPoolInner<T>, _>(|_key, old_value| {
|
|
translated.saturating_inc();
|
|
Some(old_value.migrate_to_v4())
|
|
});
|
|
|
|
StorageVersion::new(4).put::<Pezpallet<T>>();
|
|
log!(info, "Upgraded {} pools, storage to version {:?}", translated, current);
|
|
|
|
// reads: translated + onchain version.
|
|
// writes: translated + current.put + initial global commission.
|
|
T::DbWeight::get().reads_writes(translated + 1, translated + 2)
|
|
} else {
|
|
log!(info, "Migration did not execute. This probably should be removed");
|
|
T::DbWeight::get().reads(1)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
Ok(Vec::new())
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
// ensure all BondedPools items now contain an `inner.commission: Commission` field.
|
|
ensure!(
|
|
BondedPools::<T>::iter().all(|(_, inner)|
|
|
// Check current
|
|
(inner.commission.current.is_none() ||
|
|
inner.commission.current.is_some()) &&
|
|
// Check max
|
|
(inner.commission.max.is_none() || inner.commission.max.is_some()) &&
|
|
// Check change_rate
|
|
(inner.commission.change_rate.is_none() ||
|
|
inner.commission.change_rate.is_some()) &&
|
|
// Check throttle_from
|
|
(inner.commission.throttle_from.is_none() ||
|
|
inner.commission.throttle_from.is_some())),
|
|
"a commission value has not been set correctly"
|
|
);
|
|
ensure!(
|
|
GlobalMaxCommission::<T>::get() == Some(U::get()),
|
|
"global maximum commission error"
|
|
);
|
|
ensure!(
|
|
Pezpallet::<T>::on_chain_storage_version() >= 4,
|
|
"nomination-pools::migration::v4: wrong storage version"
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub mod v3 {
|
|
use super::*;
|
|
|
|
/// This migration removes stale bonded-pool metadata, if any.
|
|
pub struct MigrateToV3<T>(core::marker::PhantomData<T>);
|
|
impl<T: Config> OnRuntimeUpgrade for MigrateToV3<T> {
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let current = Pezpallet::<T>::in_code_storage_version();
|
|
let onchain = Pezpallet::<T>::on_chain_storage_version();
|
|
|
|
if onchain == 2 {
|
|
log!(
|
|
info,
|
|
"Running migration with in-code storage version {:?} / onchain {:?}",
|
|
current,
|
|
onchain
|
|
);
|
|
|
|
let mut metadata_iterated = 0u64;
|
|
let mut metadata_removed = 0u64;
|
|
Metadata::<T>::iter_keys()
|
|
.filter(|id| {
|
|
metadata_iterated += 1;
|
|
!BondedPools::<T>::contains_key(&id)
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.into_iter()
|
|
.for_each(|id| {
|
|
metadata_removed += 1;
|
|
Metadata::<T>::remove(&id);
|
|
});
|
|
StorageVersion::new(3).put::<Pezpallet<T>>();
|
|
// metadata iterated + bonded pools read + a storage version read
|
|
let total_reads = metadata_iterated * 2 + 1;
|
|
// metadata removed + a storage version write
|
|
let total_writes = metadata_removed + 1;
|
|
T::DbWeight::get().reads_writes(total_reads, total_writes)
|
|
} else {
|
|
log!(info, "MigrateToV3 should be removed");
|
|
T::DbWeight::get().reads(1)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
Ok(Vec::new())
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
ensure!(
|
|
Metadata::<T>::iter_keys().all(|id| BondedPools::<T>::contains_key(&id)),
|
|
"not all of the stale metadata has been removed"
|
|
);
|
|
ensure!(
|
|
Pezpallet::<T>::on_chain_storage_version() >= 3,
|
|
"nomination-pools::migration::v3: wrong storage version"
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub mod v2 {
|
|
use super::*;
|
|
use pezsp_runtime::Perbill;
|
|
|
|
#[test]
|
|
fn migration_assumption_is_correct() {
|
|
// this migrations cleans all the reward accounts to contain exactly ed, and all members
|
|
// having no claimable rewards. In this state, all fields of the `RewardPool` and
|
|
// `member.last_recorded_reward_counter` are all zero.
|
|
use crate::mock::*;
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
let join = |x| {
|
|
Currency::set_balance(&x, Balances::minimum_balance() + 10);
|
|
pezframe_support::assert_ok!(Pools::join(RuntimeOrigin::signed(x), 10, 1));
|
|
};
|
|
|
|
assert_eq!(BondedPool::<Runtime>::get(1).unwrap().points, 10);
|
|
assert_eq!(
|
|
RewardPools::<Runtime>::get(1).unwrap(),
|
|
RewardPool { ..Default::default() }
|
|
);
|
|
assert_eq!(
|
|
PoolMembers::<Runtime>::get(10).unwrap().last_recorded_reward_counter,
|
|
Zero::zero()
|
|
);
|
|
|
|
join(20);
|
|
assert_eq!(BondedPool::<Runtime>::get(1).unwrap().points, 20);
|
|
assert_eq!(
|
|
RewardPools::<Runtime>::get(1).unwrap(),
|
|
RewardPool { ..Default::default() }
|
|
);
|
|
assert_eq!(
|
|
PoolMembers::<Runtime>::get(10).unwrap().last_recorded_reward_counter,
|
|
Zero::zero()
|
|
);
|
|
assert_eq!(
|
|
PoolMembers::<Runtime>::get(20).unwrap().last_recorded_reward_counter,
|
|
Zero::zero()
|
|
);
|
|
|
|
join(30);
|
|
assert_eq!(BondedPool::<Runtime>::get(1).unwrap().points, 30);
|
|
assert_eq!(
|
|
RewardPools::<Runtime>::get(1).unwrap(),
|
|
RewardPool { ..Default::default() }
|
|
);
|
|
assert_eq!(
|
|
PoolMembers::<Runtime>::get(10).unwrap().last_recorded_reward_counter,
|
|
Zero::zero()
|
|
);
|
|
assert_eq!(
|
|
PoolMembers::<Runtime>::get(20).unwrap().last_recorded_reward_counter,
|
|
Zero::zero()
|
|
);
|
|
assert_eq!(
|
|
PoolMembers::<Runtime>::get(30).unwrap().last_recorded_reward_counter,
|
|
Zero::zero()
|
|
);
|
|
});
|
|
}
|
|
|
|
#[derive(Decode)]
|
|
pub struct OldRewardPool<B> {
|
|
pub balance: B,
|
|
pub total_earnings: B,
|
|
pub points: U256,
|
|
}
|
|
|
|
#[derive(Decode)]
|
|
pub struct OldPoolMember<T: Config> {
|
|
pub pool_id: PoolId,
|
|
pub points: BalanceOf<T>,
|
|
pub reward_pool_total_earnings: BalanceOf<T>,
|
|
pub unbonding_eras: BoundedBTreeMap<EraIndex, BalanceOf<T>, T::MaxUnbonding>,
|
|
}
|
|
|
|
/// Migrate the pool reward scheme to the new version, as per
|
|
/// <https://github.com/pezkuwichain/pezkuwi-sdk/issues/206.>.
|
|
pub struct MigrateToV2<T>(core::marker::PhantomData<T>);
|
|
impl<T: Config> MigrateToV2<T> {
|
|
fn run(current: StorageVersion) -> Weight {
|
|
let mut reward_pools_translated = 0u64;
|
|
let mut members_translated = 0u64;
|
|
// just for logging.
|
|
let mut total_value_locked = BalanceOf::<T>::zero();
|
|
let mut total_points_locked = BalanceOf::<T>::zero();
|
|
|
|
// store each member of the pool, with their active points. In the process, migrate
|
|
// their data as well.
|
|
let mut temp_members = BTreeMap::<PoolId, Vec<(T::AccountId, BalanceOf<T>)>>::new();
|
|
PoolMembers::<T>::translate::<OldPoolMember<T>, _>(|key, old_member| {
|
|
let id = old_member.pool_id;
|
|
temp_members.entry(id).or_default().push((key, old_member.points));
|
|
|
|
total_points_locked += old_member.points;
|
|
members_translated += 1;
|
|
Some(PoolMember::<T> {
|
|
last_recorded_reward_counter: Zero::zero(),
|
|
pool_id: old_member.pool_id,
|
|
points: old_member.points,
|
|
unbonding_eras: old_member.unbonding_eras,
|
|
})
|
|
});
|
|
|
|
// translate all reward pools. In the process, do the last payout as well.
|
|
RewardPools::<T>::translate::<OldRewardPool<BalanceOf<T>>, _>(
|
|
|id, _old_reward_pool| {
|
|
// each pool should have at least one member.
|
|
let members = match temp_members.get(&id) {
|
|
Some(x) => x,
|
|
None => {
|
|
log!(error, "pool {} has no member! deleting it..", id);
|
|
return None;
|
|
},
|
|
};
|
|
let bonded_pool = match BondedPools::<T>::get(id) {
|
|
Some(x) => x,
|
|
None => {
|
|
log!(error, "pool {} has no bonded pool! deleting it..", id);
|
|
return None;
|
|
},
|
|
};
|
|
|
|
let accumulated_reward = RewardPool::<T>::current_balance(id);
|
|
let reward_account = Pezpallet::<T>::generate_reward_account(id);
|
|
let mut sum_paid_out = BalanceOf::<T>::zero();
|
|
|
|
members
|
|
.into_iter()
|
|
.filter_map(|(who, points)| {
|
|
let bonded_pool = match BondedPool::<T>::get(id) {
|
|
Some(x) => x,
|
|
None => {
|
|
log!(error, "pool {} for member {:?} does not exist!", id, who);
|
|
return None;
|
|
},
|
|
};
|
|
|
|
total_value_locked += bonded_pool.points_to_balance(*points);
|
|
let portion = Perbill::from_rational(*points, bonded_pool.points);
|
|
let last_claim = portion * accumulated_reward;
|
|
|
|
log!(
|
|
debug,
|
|
"{:?} has {:?} ({:?}) of pool {} with total reward of {:?}",
|
|
who,
|
|
portion,
|
|
last_claim,
|
|
id,
|
|
accumulated_reward
|
|
);
|
|
|
|
if last_claim.is_zero() {
|
|
None
|
|
} else {
|
|
Some((who, last_claim))
|
|
}
|
|
})
|
|
.for_each(|(who, last_claim)| {
|
|
let outcome = T::Currency::transfer(
|
|
&reward_account,
|
|
&who,
|
|
last_claim,
|
|
Preservation::Preserve,
|
|
);
|
|
|
|
if let Err(reason) = outcome {
|
|
log!(warn, "last reward claim failed due to {:?}", reason,);
|
|
} else {
|
|
sum_paid_out = sum_paid_out.saturating_add(last_claim);
|
|
}
|
|
|
|
Pezpallet::<T>::deposit_event(Event::<T>::PaidOut {
|
|
member: who.clone(),
|
|
pool_id: id,
|
|
payout: last_claim,
|
|
});
|
|
});
|
|
|
|
// this can only be because of rounding down, or because the person we
|
|
// wanted to pay their reward to could not accept it (dust).
|
|
let leftover = accumulated_reward.saturating_sub(sum_paid_out);
|
|
if !leftover.is_zero() {
|
|
// pay it all to depositor.
|
|
let o = T::Currency::transfer(
|
|
&reward_account,
|
|
&bonded_pool.roles.depositor,
|
|
leftover,
|
|
Preservation::Preserve,
|
|
);
|
|
log!(warn, "paying {:?} leftover to the depositor: {:?}", leftover, o);
|
|
}
|
|
|
|
// finally, migrate the reward pool.
|
|
reward_pools_translated += 1;
|
|
|
|
Some(RewardPool {
|
|
last_recorded_reward_counter: Zero::zero(),
|
|
last_recorded_total_payouts: Zero::zero(),
|
|
total_rewards_claimed: Zero::zero(),
|
|
total_commission_claimed: Zero::zero(),
|
|
total_commission_pending: Zero::zero(),
|
|
})
|
|
},
|
|
);
|
|
|
|
log!(
|
|
info,
|
|
"Upgraded {} members, {} reward pools, TVL {:?} TPL {:?}, storage to version {:?}",
|
|
members_translated,
|
|
reward_pools_translated,
|
|
total_value_locked,
|
|
total_points_locked,
|
|
current
|
|
);
|
|
current.put::<Pezpallet<T>>();
|
|
|
|
T::DbWeight::get().reads_writes(members_translated + 1, reward_pools_translated + 1)
|
|
}
|
|
}
|
|
|
|
impl<T: Config> OnRuntimeUpgrade for MigrateToV2<T> {
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let current = Pezpallet::<T>::in_code_storage_version();
|
|
let onchain = Pezpallet::<T>::on_chain_storage_version();
|
|
|
|
log!(
|
|
info,
|
|
"Running migration with in-code storage version {:?} / onchain {:?}",
|
|
current,
|
|
onchain
|
|
);
|
|
|
|
if current == 2 && onchain == 1 {
|
|
Self::run(current)
|
|
} else {
|
|
log!(info, "MigrateToV2 did not executed. This probably should be removed");
|
|
T::DbWeight::get().reads(1)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
// all reward accounts must have more than ED.
|
|
RewardPools::<T>::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> {
|
|
ensure!(
|
|
<T::Currency as pezframe_support::traits::fungible::Inspect<T::AccountId>>::balance(
|
|
&Pezpallet::<T>::generate_reward_account(id)
|
|
) >= T::Currency::minimum_balance(),
|
|
"Reward accounts must have greater balance than ED."
|
|
);
|
|
Ok(())
|
|
})?;
|
|
|
|
Ok(Vec::new())
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
// new version must be set.
|
|
ensure!(
|
|
Pezpallet::<T>::on_chain_storage_version() == 2,
|
|
"The onchain version must be updated after the migration."
|
|
);
|
|
|
|
// no reward or bonded pool has been skipped.
|
|
ensure!(
|
|
RewardPools::<T>::iter().count() as u32 == RewardPools::<T>::count(),
|
|
"The count of reward pools must remain the same after the migration."
|
|
);
|
|
ensure!(
|
|
BondedPools::<T>::iter().count() as u32 == BondedPools::<T>::count(),
|
|
"The count of reward pools must remain the same after the migration."
|
|
);
|
|
|
|
// all reward pools must have exactly ED in them. This means no reward can be claimed,
|
|
// and that setting reward counters all over the board to zero will work henceforth.
|
|
RewardPools::<T>::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> {
|
|
ensure!(
|
|
RewardPool::<T>::current_balance(id) == Zero::zero(),
|
|
"Reward pool balance must be zero.",
|
|
);
|
|
Ok(())
|
|
})?;
|
|
|
|
log!(info, "post upgrade hook for MigrateToV2 executed.");
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub mod v1 {
|
|
use super::*;
|
|
|
|
#[derive(Decode)]
|
|
pub struct OldPoolRoles<AccountId> {
|
|
pub depositor: AccountId,
|
|
pub root: AccountId,
|
|
pub nominator: AccountId,
|
|
pub bouncer: AccountId,
|
|
}
|
|
|
|
impl<AccountId> OldPoolRoles<AccountId> {
|
|
fn migrate_to_v1(self) -> PoolRoles<AccountId> {
|
|
PoolRoles {
|
|
depositor: self.depositor,
|
|
root: Some(self.root),
|
|
nominator: Some(self.nominator),
|
|
bouncer: Some(self.bouncer),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Decode)]
|
|
pub struct OldBondedPoolInner<T: Config> {
|
|
pub points: BalanceOf<T>,
|
|
pub state: PoolState,
|
|
pub member_counter: u32,
|
|
pub roles: OldPoolRoles<T::AccountId>,
|
|
}
|
|
|
|
impl<T: Config> OldBondedPoolInner<T> {
|
|
fn migrate_to_v1(self) -> BondedPoolInner<T> {
|
|
// Note: `commission` field not introduced to `BondedPoolInner` until
|
|
// migration 4.
|
|
BondedPoolInner {
|
|
points: self.points,
|
|
commission: Commission::default(),
|
|
member_counter: self.member_counter,
|
|
state: self.state,
|
|
roles: self.roles.migrate_to_v1(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Trivial migration which makes the roles of each pool optional.
|
|
///
|
|
/// Note: The depositor is not optional since they can never change.
|
|
pub struct MigrateToV1<T>(core::marker::PhantomData<T>);
|
|
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let current = Pezpallet::<T>::in_code_storage_version();
|
|
let onchain = Pezpallet::<T>::on_chain_storage_version();
|
|
|
|
log!(
|
|
info,
|
|
"Running migration with in-code storage version {:?} / onchain {:?}",
|
|
current,
|
|
onchain
|
|
);
|
|
|
|
if current == 1 && onchain == 0 {
|
|
// this is safe to execute on any runtime that has a bounded number of pools.
|
|
let mut translated = 0u64;
|
|
BondedPools::<T>::translate::<OldBondedPoolInner<T>, _>(|_key, old_value| {
|
|
translated.saturating_inc();
|
|
Some(old_value.migrate_to_v1())
|
|
});
|
|
|
|
current.put::<Pezpallet<T>>();
|
|
|
|
log!(info, "Upgraded {} pools, storage to version {:?}", translated, current);
|
|
|
|
T::DbWeight::get().reads_writes(translated + 1, translated + 1)
|
|
} else {
|
|
log!(info, "Migration did not executed. This probably should be removed");
|
|
T::DbWeight::get().reads(1)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
|
// new version must be set.
|
|
ensure!(
|
|
Pezpallet::<T>::on_chain_storage_version() == 1,
|
|
"The onchain version must be updated after the migration."
|
|
);
|
|
Pezpallet::<T>::try_state(pezframe_system::Pezpallet::<T>::block_number())?;
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
mod helpers {
|
|
use super::*;
|
|
|
|
pub(crate) fn calculate_tvl_by_total_stake<T: Config>() -> BalanceOf<T> {
|
|
BondedPools::<T>::iter_keys()
|
|
.map(|id| {
|
|
T::StakeAdapter::total_stake(Pool::from(Pezpallet::<T>::generate_bonded_account(
|
|
id,
|
|
)))
|
|
})
|
|
.reduce(|acc, total_balance| acc + total_balance)
|
|
.unwrap_or_default()
|
|
}
|
|
}
|