diff --git a/pallets/system-staking/src/benchmarking.rs b/pallets/system-staking/src/benchmarking.rs index ab8dc86c4..d40ef8b1a 100644 --- a/pallets/system-staking/src/benchmarking.rs +++ b/pallets/system-staking/src/benchmarking.rs @@ -35,7 +35,7 @@ benchmarks! { assert_ok!(SystemStaking::::token_config( RawOrigin::Root.into(), KSM, - Some(1), + Some(BlockNumberFor::::from(1u32)), Some(Permill::from_percent(80)), Some(false), Some(BalanceOf::::unique_saturated_from(1000u128)), @@ -45,7 +45,7 @@ benchmarks! { assert_ok!(SystemStaking::::token_config( RawOrigin::Root.into(), MOVR, - Some(2), + Some(BlockNumberFor::::from(2u32)), Some(Permill::from_percent(80)), Some(false), Some(BalanceOf::::unique_saturated_from(1000u128)), @@ -66,14 +66,14 @@ benchmarks! { const KSM: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); let token_amount = BalanceOf::::unique_saturated_from(1000u128); let pool_id = PoolId::from(1u32); - }: _(RawOrigin::Root, KSM, Some(1), Some(Permill::from_percent(80)),Some(false),Some(token_amount),Some(vec![pool_id]),Some(vec![Perbill::from_percent(100)])) + }: _(RawOrigin::Root, KSM, Some(BlockNumberFor::::from(1u32)), Some(Permill::from_percent(80)),Some(false),Some(token_amount),Some(vec![pool_id]),Some(vec![Perbill::from_percent(100)])) refresh_token_info { const KSM: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); assert_ok!(SystemStaking::::token_config( RawOrigin::Root.into(), KSM, - Some(1), + Some(BlockNumberFor::::from(1u32)), Some(Permill::from_percent(80)), Some(false), Some(BalanceOf::::unique_saturated_from(1000u128)), @@ -87,7 +87,7 @@ benchmarks! { assert_ok!(SystemStaking::::token_config( RawOrigin::Root.into(), KSM, - Some(1), + Some(BlockNumberFor::::from(1u32)), Some(Permill::from_percent(80)), Some(false), Some(BalanceOf::::unique_saturated_from(1000u128)), @@ -104,7 +104,7 @@ benchmarks! { assert_ok!(SystemStaking::::token_config( RawOrigin::Root.into(), KSM, - Some(1), + Some(BlockNumberFor::::from(1u32)), Some(Permill::from_percent(80)), Some(false), Some(BalanceOf::::unique_saturated_from(1000u128)), @@ -120,7 +120,7 @@ benchmarks! { assert_ok!(SystemStaking::::token_config( RawOrigin::Root.into(), KSM, - Some(1), + Some(BlockNumberFor::::from(1u32)), Some(Permill::from_percent(80)), Some(false), Some(BalanceOf::::unique_saturated_from(1000u128)), @@ -137,7 +137,7 @@ benchmarks! { assert_ok!(SystemStaking::::token_config( RawOrigin::Root.into(), KSM, - Some(1), + Some(BlockNumberFor::::from(1u32)), Some(Permill::from_percent(80)), Some(false), Some(BalanceOf::::unique_saturated_from(1000u128)), diff --git a/pallets/system-staking/src/lib.rs b/pallets/system-staking/src/lib.rs index a496f8e4e..5a7cbd392 100644 --- a/pallets/system-staking/src/lib.rs +++ b/pallets/system-staking/src/lib.rs @@ -23,6 +23,7 @@ pub use weights::WeightInfo; use bifrost_primitives::{CurrencyId, FarmingInfo, PoolId, VtokenMintingInterface}; pub use frame_support::weights::Weight; use frame_support::{dispatch::DispatchResultWithPostInfo, traits::Get, PalletId}; +use frame_system::pallet_prelude::BlockNumberFor; use orml_traits::MultiCurrency; pub use pallet::*; use sp_runtime::{ @@ -41,6 +42,8 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +pub mod migrations; + pub type AccountIdOf = ::AccountId; pub type CurrencyIdOf = <::MultiCurrency as MultiCurrency< @@ -98,13 +101,18 @@ pub mod pallet { #[pallet::constant] type PalletId: Get; - /// 1500 + /// The number of blocks per round, as defined in the runtime. + /// + /// This value is set to 1500 in the runtime configuration. #[pallet::constant] type BlocksPerRound: Get; } + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); /// Current Round Information @@ -113,8 +121,13 @@ pub mod pallet { /// The tokenInfo for each currency #[pallet::storage] - pub(crate) type TokenStatus = - StorageMap<_, Twox64Concat, CurrencyIdOf, TokenInfo>, OptionQuery>; + pub(crate) type TokenStatus = StorageMap< + _, + Twox64Concat, + CurrencyIdOf, + TokenInfo, BlockNumberFor>, + OptionQuery, + >; /// All token sets #[pallet::storage] @@ -124,20 +137,38 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - NewRound { - current: RoundIndex, - first: BlockNumberFor, - length: u32, - }, + /// A new staking round has started. + /// + /// - `current`: The index of the current round. + /// - `first`: The block number at which this round started. + /// - `length`: The length of the round in blocks. + NewRound { current: RoundIndex, first: BlockNumberFor, length: u32 }, + /// Configuration of a token has been changed. + /// + /// - `token`: The identifier of the token whose configuration changed. + /// - `exec_delay`: The delay in blocks before the changes take effect. + /// - `system_stakable_farming_rate`: The farming rate applied to system-stakable tokens. + /// - `add_or_sub`: Whether to add or subtract from the stakable farming rate. + /// - `system_stakable_base`: The base value of system-stakable assets. + /// - `farming_poolids`: List of pool IDs related to the token. + /// - `lptoken_rates`: List of rates for liquidity provider (LP) tokens. TokenConfigChanged { token: CurrencyIdOf, - exec_delay: u32, + exec_delay: BlockNumberFor, system_stakable_farming_rate: Permill, add_or_sub: bool, system_stakable_base: BalanceOf, - farming_poolids: Vec, - lptoken_rates: Vec, + farming_poolids: BoundedVec>, + lptoken_rates: BoundedVec>, }, + /// A deposit operation has failed. + /// + /// - `token`: The identifier of the token being deposited. + /// - `amount`: The amount of the token to be deposited. + /// - `farming_staking_amount`: The amount staked in the farming pool. + /// - `system_stakable_amount`: The amount staked in the system-stakable pool. + /// - `system_shadow_amount`: The amount shadow-staked in the system. + /// - `pending_redeem_amount`: The amount pending redemption. DepositFailed { token: CurrencyIdOf, amount: BalanceOf, @@ -146,6 +177,15 @@ pub mod pallet { system_shadow_amount: BalanceOf, pending_redeem_amount: BalanceOf, }, + + /// Minting operation succeeded. + /// + /// - `token`: The identifier of the token being minted. + /// - `amount`: The amount of the token to be minted. + /// - `farming_staking_amount`: The amount staked in the farming pool. + /// - `system_stakable_amount`: The amount staked in the system-stakable pool. + /// - `system_shadow_amount`: The amount shadow-staked in the system. + /// - `pending_redeem_amount`: The amount pending redemption. MintSuccess { token: CurrencyIdOf, amount: BalanceOf, @@ -154,6 +194,10 @@ pub mod pallet { system_shadow_amount: BalanceOf, pending_redeem_amount: BalanceOf, }, + /// Minting operation failed. + /// + /// # Parameters + /// (Same as MintSuccess) MintFailed { token: CurrencyIdOf, amount: BalanceOf, @@ -162,6 +206,10 @@ pub mod pallet { system_shadow_amount: BalanceOf, pending_redeem_amount: BalanceOf, }, + /// Withdrawal operation succeeded. + /// + /// # Parameters + /// (Same as MintSuccess) WithdrawSuccess { token: CurrencyIdOf, amount: BalanceOf, @@ -170,6 +218,10 @@ pub mod pallet { system_shadow_amount: BalanceOf, pending_redeem_amount: BalanceOf, }, + /// Withdrawal operation failed. + /// + /// # Parameters + /// (Same as MintSuccess) WithdrawFailed { token: CurrencyIdOf, amount: BalanceOf, @@ -178,6 +230,11 @@ pub mod pallet { system_shadow_amount: BalanceOf, pending_redeem_amount: BalanceOf, }, + + /// A redemption operation has succeeded. + /// + /// # Parameters + /// (Same as MintSuccess) Redeemed { token: CurrencyIdOf, amount: BalanceOf, @@ -186,6 +243,10 @@ pub mod pallet { system_shadow_amount: BalanceOf, pending_redeem_amount: BalanceOf, }, + /// A redemption operation has failed. + /// + /// # Parameters + /// (Same as MintSuccess) RedeemFailed { token: CurrencyIdOf, amount: BalanceOf, @@ -194,12 +255,24 @@ pub mod pallet { system_shadow_amount: BalanceOf, pending_redeem_amount: BalanceOf, }, - VtokenNotFound { - token: CurrencyIdOf, - }, - TokenInfoRefreshed { - token: CurrencyIdOf, - }, + /// The specified token could not be found. + /// + /// - `token`: The identifier of the token that was not found. + VtokenNotFound { token: CurrencyIdOf }, + /// Token information has been refreshed. + /// + /// - `token`: The identifier of the token whose information was refreshed. + TokenInfoRefreshed { token: CurrencyIdOf }, + /// A payout has been made. + /// + /// - `token`: The identifier of the token involved in the payout. + /// - `vtoken`: The identifier of the vtoken involved. + /// - `from`: The account from which the payout originated. + /// - `to`: The account to which the payout was made. + /// - `amount`: The total amount of the payout. + /// - `free`: The amount of free balance after the payout. + /// - `vfree`: The amount of vtoken free balance after the payout. + /// - `shadow`: The shadow balance after the payout. Payout { token: CurrencyIdOf, vtoken: CurrencyIdOf, @@ -224,6 +297,8 @@ pub mod pallet { TokenInfoNotFound, /// payout error PayoutFailed, + /// Error converting Vec to BoundedVec. + ConversionError, } #[pallet::hooks] @@ -296,7 +371,7 @@ pub mod pallet { pub fn token_config( origin: OriginFor, token: CurrencyIdOf, - exec_delay: Option, // TODO: blocknum + exec_delay: Option>, system_stakable_farming_rate: Option, add_or_sub: Option, system_stakable_base: Option>, @@ -311,7 +386,7 @@ pub mod pallet { state } else { new_token = true; - >>::default() + , BlockNumberFor>>::default() }; // Set token_info.new_config @@ -319,16 +394,12 @@ pub mod pallet { // Set token_info.new_config.exec_delay = exec_delay if let Some(exec_delay) = exec_delay { - ensure!(exec_delay != 0, Error::::InvalidTokenConfig); + ensure!(!exec_delay.is_zero(), Error::::InvalidTokenConfig); token_info.new_config.exec_delay = exec_delay; } // Set token_info.new_config.system_stakable_farming_rate = system_stakable_farming_rate if let Some(system_stakable_farming_rate) = system_stakable_farming_rate { - ensure!( - system_stakable_farming_rate >= Permill::zero(), - Error::::InvalidTokenConfig - ); token_info.new_config.system_stakable_farming_rate = system_stakable_farming_rate; } @@ -349,7 +420,9 @@ pub mod pallet { farming_poolids.len() as u32 <= T::MaxFarmingPoolIdLen::get(), Error::::ExceedMaxFarmingPoolidLen ); - token_info.new_config.farming_poolids = farming_poolids.clone(); + token_info.new_config.farming_poolids = + BoundedVec::try_from(farming_poolids.clone()) + .map_err(|_| Error::::ConversionError)?; } // Set token_info.new_config.lptoken_rates = lptoken_rates @@ -359,7 +432,8 @@ pub mod pallet { lptoken_rates.len() as u32 <= T::MaxFarmingPoolIdLen::get(), Error::::ExceedMaxFarmingPoolidLen ); - token_info.new_config.lptoken_rates = lptoken_rates.clone(); + token_info.new_config.lptoken_rates = BoundedVec::try_from(lptoken_rates.clone()) + .map_err(|_| Error::::ConversionError)?; } // Update token info @@ -487,7 +561,7 @@ pub mod pallet { impl Pallet { fn process_token_info( account: AccountIdOf, - mut token_info: TokenInfo>, + mut token_info: TokenInfo, BlockNumberFor>, token_id: CurrencyIdOf, ) -> DispatchResultWithPostInfo { // Query farming info @@ -561,11 +635,9 @@ impl Pallet { system_shadow_amount: token_info.system_shadow_amount, pending_redeem_amount: token_info.pending_redeem_amount, }); - } - // Check stakable_amount < (system_shadow_amount - pending_redeem_amount) ===> redeem vksm , // update pending_redeem_amount += token_amount - if stakable_amount < + } else if stakable_amount < token_info.system_shadow_amount.saturating_sub(token_info.pending_redeem_amount) { // redeem_amount = system_shadow_amount - pending_redeem_amount - stakable_amount @@ -589,7 +661,7 @@ impl Pallet { let new_token_info = if let Some(state) = >::get(&token_id) { state } else { - >>::default() + , BlockNumberFor>>::default() }; token_info.pending_redeem_amount = new_token_info.pending_redeem_amount; } @@ -618,7 +690,7 @@ impl Pallet { let mut token_info = if let Some(state) = >::get(&token_id) { state } else { - >>::default() + , BlockNumberFor>>::default() }; // pending_redeem_amount -= token_amount @@ -682,7 +754,7 @@ impl Pallet { let mut token_info = if let Some(state) = >::get(&token_id) { state } else { - >>::default() + , BlockNumberFor>>::default() }; // pending_redeem_amount += token_amount diff --git a/pallets/system-staking/src/migrations/mod.rs b/pallets/system-staking/src/migrations/mod.rs new file mode 100644 index 000000000..379d174c5 --- /dev/null +++ b/pallets/system-staking/src/migrations/mod.rs @@ -0,0 +1,19 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pub mod v1; diff --git a/pallets/system-staking/src/migrations/v1.rs b/pallets/system-staking/src/migrations/v1.rs new file mode 100644 index 000000000..4468a16ae --- /dev/null +++ b/pallets/system-staking/src/migrations/v1.rs @@ -0,0 +1,130 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::*; +use frame_support::{ + ensure, + pallet_prelude::StorageVersion, + traits::{GetStorageVersion, OnRuntimeUpgrade}, +}; +use parity_scale_codec::{Decode, Encode}; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + +const LOG_TARGET: &str = "system-staking::migration"; + +pub struct MigrateToV1(sp_std::marker::PhantomData); +impl OnRuntimeUpgrade for MigrateToV1 { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + // Check the storage version + let onchain_version = Pallet::::on_chain_storage_version(); + if onchain_version < 1 { + // Transform storage values + // We transform the storage values from the old into the new format. + log::info!(target: LOG_TARGET, "Start to migrate TokenStatus storage..."); + TokenStatus::::translate::, BlockNumberFor>, _>( + |k: CurrencyId, old_token_info: TokenInfo, BlockNumberFor>| { + log::info!(target: LOG_TARGET, "Migrated to boundedvec for {:?}...", k); + + let mut new_token_info = + , BlockNumberFor>>::default(); + + new_token_info.farming_staking_amount = old_token_info.farming_staking_amount; + new_token_info.system_stakable_amount = old_token_info.system_stakable_amount; + new_token_info.system_shadow_amount = old_token_info.system_shadow_amount; + new_token_info.pending_redeem_amount = old_token_info.pending_redeem_amount; + + new_token_info.current_config.exec_delay = + BlockNumberFor::::from(old_token_info.current_config.exec_delay); + new_token_info.current_config.system_stakable_farming_rate = + old_token_info.current_config.system_stakable_farming_rate; + new_token_info.current_config.lptoken_rates = + BoundedVec::try_from(old_token_info.current_config.lptoken_rates) + .map_err(|e| { log::error!("Failed to convert old current_config.lptoken_rates into BoundedVec during migration for {:?}: {:?}", k, e) }) + .unwrap(); + new_token_info.current_config.add_or_sub = + old_token_info.current_config.add_or_sub; + new_token_info.current_config.system_stakable_base = + old_token_info.current_config.system_stakable_base; + new_token_info.current_config.farming_poolids = + BoundedVec::try_from(old_token_info.current_config.farming_poolids) + .map_err(|e| { log::error!("Failed to convert old current_config.farming_poolids into BoundedVec during migration for {:?}: {:?}", k, e) }) + .unwrap(); + + new_token_info.new_config.exec_delay = + BlockNumberFor::::from(old_token_info.new_config.exec_delay); + new_token_info.new_config.system_stakable_farming_rate = + old_token_info.new_config.system_stakable_farming_rate; + new_token_info.new_config.lptoken_rates = + BoundedVec::try_from(old_token_info.new_config.lptoken_rates) + .map_err(|e| { log::error!("Failed to convert old new_config.lptoken_rates into BoundedVec during migration for {:?}: {:?}", k, e) }) + .unwrap(); + new_token_info.new_config.add_or_sub = old_token_info.new_config.add_or_sub; + new_token_info.new_config.system_stakable_base = + old_token_info.new_config.system_stakable_base; + new_token_info.new_config.farming_poolids = + BoundedVec::try_from(old_token_info.new_config.farming_poolids) + .map_err(|e| { log::error!("Failed to convert old new_config.farming_poolids into BoundedVec during migration for {:?}: {:?}", k, e) }) + .unwrap(); + + Some(new_token_info) + }, + ); + + // Update the storage version + StorageVersion::new(1).put::>(); + + // Return the consumed weight + let count = TokenStatus::::iter().count(); + Weight::from(T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)) + } else { + // We don't do anything here. + Weight::zero() + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + let cnt = TokenStatus::::iter().count(); + // print out the pre-migrate storage count + log::info!(target: LOG_TARGET, "TokenStatus pre-migrate storage count: {:?}", cnt); + Ok((cnt as u64).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(cnt: Vec) -> Result<(), TryRuntimeError> { + let new_count = TokenStatus::::iter().count(); + + let old_count: u64 = Decode::decode(&mut cnt.as_slice()) + .expect("the state parameter should be something that was generated by pre_upgrade"); + + // print out the post-migrate storage count + log::info!( + target: LOG_TARGET, + "TokenStatus post-migrate storage count: {:?}", + new_count + ); + + ensure!( + new_count as u64 == old_count, + "Post-migration storage count does not match pre-migration count" + ); + + Ok(()) + } +} diff --git a/pallets/system-staking/src/mock.rs b/pallets/system-staking/src/mock.rs index a50e2f26a..dbb3fd60a 100644 --- a/pallets/system-staking/src/mock.rs +++ b/pallets/system-staking/src/mock.rs @@ -38,7 +38,7 @@ use frame_system::{EnsureRoot, EnsureSignedBy}; use orml_traits::{location::RelativeReserveProvider, parameter_type_with_key}; use sp_core::ConstU32; use sp_runtime::{ - traits::{ConvertInto, IdentityLookup}, + traits::{AccountIdConversion, ConvertInto, IdentityLookup}, AccountId32, BuildStorage, }; use sp_std::vec; @@ -47,6 +47,7 @@ use xcm_builder::{FixedWeightBounds, FrameTransactionalProcessor}; use xcm_executor::XcmExecutor; use crate as system_staking; +use crate::Config; pub type BlockNumber = u64; pub type Amount = i128; @@ -407,6 +408,7 @@ impl ExtBuilder { } pub fn one_hundred_for_alice_n_bob(self) -> Self { + let pallet_account = ::PalletId::get().into_account_truncating(); self.balances(vec![ (ALICE, BNC, 100), (BOB, BNC, 100), @@ -417,6 +419,7 @@ impl ExtBuilder { (BOB, VKSM, 1000), (BOB, KSM, 10000000000), (BOB, MOVR, 1000000000000000000000), + (pallet_account, VKSM, 100), ]) } diff --git a/pallets/system-staking/src/tests.rs b/pallets/system-staking/src/tests.rs index 75544e3ba..cabff83e5 100644 --- a/pallets/system-staking/src/tests.rs +++ b/pallets/system-staking/src/tests.rs @@ -21,7 +21,7 @@ use bifrost_asset_registry::AssetMetadata; use bifrost_primitives::{TimeUnit, TokenInfo, VtokenMintingOperator}; use bifrost_runtime_common::milli; use frame_support::{ - assert_ok, + assert_noop, assert_ok, sp_runtime::{Perbill, Permill}, }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; @@ -32,7 +32,7 @@ fn token_config_should_work() { assert_ok!(SystemStaking::token_config( RuntimeOrigin::root(), KSM, - Some(1), + Some(BlockNumberFor::::from(1u32)), Some(Permill::from_percent(80)), Some(false), Some(100), @@ -151,6 +151,171 @@ fn refresh_token_info_should_work() { }); } +#[test] +fn payout_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + let (pid, _tokens) = init_farming_no_gauge(); + asset_registry(); + + assert_ok!(VtokenMinting::set_minimum_mint(RuntimeOrigin::signed(ALICE), KSM, 0)); + pub const FEE: Permill = Permill::from_percent(5); + assert_ok!(VtokenMinting::set_fees(RuntimeOrigin::root(), FEE, FEE)); + + assert_ok!(SystemStaking::token_config( + RuntimeOrigin::root(), + KSM, + Some(1), + Some(Permill::from_percent(80)), + Some(false), + Some(100), + Some(vec![pid]), + Some(vec![Perbill::from_percent(100)]), + )); + + assert_ok!(VtokenMinting::mint( + RuntimeOrigin::signed(ALICE), + KSM, + 1000, + BoundedVec::default(), + Some(0u32), + )); + + let pallet_account = ::PalletId::get().into_account_truncating(); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 100); + + assert_ok!(SystemStaking::payout(RuntimeOrigin::root(), KSM)); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 1); + + let treasury_balance = Currencies::free_balance(VKSM, &TreasuryAccount::get()); + assert_eq!(treasury_balance, 99); + }); +} + +#[test] +fn payout_should_fail() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + + assert_ok!(VtokenMinting::set_minimum_mint(RuntimeOrigin::signed(ALICE), KSM, 0)); + pub const FEE: Permill = Permill::from_percent(5); + assert_ok!(VtokenMinting::set_fees(RuntimeOrigin::root(), FEE, FEE)); + + assert_ok!(VtokenMinting::mint( + RuntimeOrigin::signed(ALICE), + KSM, + 1000, + BoundedVec::default(), + Some(0u32), + )); + + assert_noop!( + SystemStaking::payout(RuntimeOrigin::root(), KSM), + Error::::TokenInfoNotFound + ); + }); +} + +#[test] +fn on_redeem_success_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + + let pallet_account = ::PalletId::get().into_account_truncating(); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 100); + + SystemStaking::on_redeem_success(VKSM, pallet_account.clone(), 10); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 90); + let mut token_info = >::get(VKSM).unwrap(); + assert_eq!(token_info.pending_redeem_amount, 0); + assert_eq!(token_info.system_shadow_amount, 0); + + token_info.pending_redeem_amount = 100; + token_info.system_shadow_amount = 100; + >::insert(&VKSM, token_info.clone()); + assert_eq!(token_info.pending_redeem_amount, 100); + assert_eq!(token_info.system_shadow_amount, 100); + + SystemStaking::on_redeem_success(VKSM, pallet_account.clone(), 90); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 0); + let token_info = >::get(VKSM).unwrap(); + assert_eq!(token_info.pending_redeem_amount, 10); + assert_eq!(token_info.system_shadow_amount, 10); + }); +} + +#[test] +fn on_refund_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + + let pallet_account = ::PalletId::get().into_account_truncating(); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 100); + + SystemStaking::on_refund(VKSM, pallet_account.clone(), 10); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 90); + let mut token_info = >::get(VKSM).unwrap(); + assert_eq!(token_info.pending_redeem_amount, 0); + assert_eq!(token_info.system_shadow_amount, 0); + + token_info.pending_redeem_amount = 100; + token_info.system_shadow_amount = 100; + >::insert(&VKSM, token_info.clone()); + assert_eq!(token_info.pending_redeem_amount, 100); + assert_eq!(token_info.system_shadow_amount, 100); + + SystemStaking::on_refund(VKSM, pallet_account.clone(), 90); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 0); + let token_info = >::get(VKSM).unwrap(); + assert_eq!(token_info.pending_redeem_amount, 10); + assert_eq!(token_info.system_shadow_amount, 10); + }); +} + +#[test] +fn on_redeemed_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + + let pallet_account = ::PalletId::get().into_account_truncating(); + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 100); + + SystemStaking::on_redeemed(pallet_account.clone(), VKSM, 10, 0, 0); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 100); + let mut token_info = >::get(VKSM).unwrap(); + assert_eq!(token_info.pending_redeem_amount, 10); + + token_info.pending_redeem_amount = 100; + >::insert(&VKSM, token_info.clone()); + assert_eq!(token_info.pending_redeem_amount, 100); + + SystemStaking::on_redeemed(pallet_account.clone(), VKSM, 10, 0, 0); + + let pallet_vfree_balance = Currencies::free_balance(VKSM, &pallet_account); + assert_eq!(pallet_vfree_balance, 100); + let token_info = >::get(VKSM).unwrap(); + assert_eq!(token_info.pending_redeem_amount, 110); + }); +} + #[test] fn round_process_token() { ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { diff --git a/pallets/system-staking/src/types.rs b/pallets/system-staking/src/types.rs index 08b7a64c4..d0ce95821 100644 --- a/pallets/system-staking/src/types.rs +++ b/pallets/system-staking/src/types.rs @@ -54,9 +54,9 @@ impl< } /// Check exec_delay match - pub fn check_delay(&self, now: B, delay: u32) -> bool { + pub fn check_delay(&self, now: B, delay: B) -> bool { //Current blockNumber - BlockNumber of Round Start == delay blockNumber ===> true - now - self.first == delay.into() && delay != 0 + now - self.first == delay && delay != 0.into() } } impl< @@ -69,7 +69,14 @@ impl< } #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct TokenInfo { +pub struct TokenInfo< + Balance: Copy, + BlockNumber: Copy + + sp_std::ops::Add + + sp_std::ops::Sub + + From + + PartialOrd, +> { /// The number of token staking in Farming pub farming_staking_amount: Balance, /// token_config.system_stakable_farming_rate(100%) * farming_staking_amount(0) +/- @@ -80,25 +87,41 @@ pub struct TokenInfo { /// Number of pending redemptions pub pending_redeem_amount: Balance, /// Current TokenConfig - pub current_config: TokenConfig, + pub current_config: TokenConfig, /// New TokenConfig - pub new_config: TokenConfig, + pub new_config: TokenConfig, } -impl Default for TokenInfo { - fn default() -> TokenInfo { +impl< + Balance: Zero + Copy, + BlockNumber: Copy + + sp_std::ops::Add + + sp_std::ops::Sub + + From + + PartialOrd, + > Default for TokenInfo +{ + fn default() -> TokenInfo { TokenInfo { farming_staking_amount: Balance::zero(), system_stakable_amount: Balance::zero(), system_shadow_amount: Balance::zero(), pending_redeem_amount: Balance::zero(), - current_config: TokenConfig::::default(), - new_config: TokenConfig::::default(), + current_config: TokenConfig::::default(), + new_config: TokenConfig::::default(), } } } -impl TokenInfo { +impl< + Balance: Copy + PartialEq, + BlockNumber: Copy + + sp_std::ops::Add + + sp_std::ops::Sub + + From + + PartialOrd, + > TokenInfo +{ pub fn check_config_change(&self) -> bool { self.current_config != self.new_config } @@ -109,30 +132,45 @@ impl TokenInfo { } #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct TokenConfig { +pub struct TokenConfig +where + BlockNumber: Copy + + sp_std::ops::Add + + sp_std::ops::Sub + + From + + PartialOrd, +{ /// Number of blocks with delayed execution - pub exec_delay: u32, + pub exec_delay: BlockNumber, /// 100 % pub system_stakable_farming_rate: Permill, /// - pub lptoken_rates: Vec, + pub lptoken_rates: BoundedVec>, /// true: add, false: sub , +/- token_config.system_stakable_base pub add_or_sub: bool, /// pub system_stakable_base: Balance, /// Farming pool ids - pub farming_poolids: Vec, + pub farming_poolids: BoundedVec>, } -impl Default for TokenConfig { - fn default() -> TokenConfig { +impl< + Balance: Zero, + BlockNumber: Copy + + sp_std::ops::Add + + sp_std::ops::Sub + + From + + PartialOrd, + > Default for TokenConfig +{ + fn default() -> TokenConfig { TokenConfig { - exec_delay: 0u32, + exec_delay: 0u32.into(), system_stakable_farming_rate: Permill::from_percent(0), - lptoken_rates: Vec::new(), + lptoken_rates: BoundedVec::default(), system_stakable_base: Balance::zero(), add_or_sub: true, // default add - farming_poolids: Vec::new(), + farming_poolids: BoundedVec::default(), } } } diff --git a/runtime/bifrost-kusama/src/lib.rs b/runtime/bifrost-kusama/src/lib.rs index b25e34213..8161785aa 100644 --- a/runtime/bifrost-kusama/src/lib.rs +++ b/runtime/bifrost-kusama/src/lib.rs @@ -1914,6 +1914,7 @@ pub mod migrations { VSBondAuctionClearPalletId, frame_support::migrations::RemovePallet, frame_support::migrations::RemovePallet, + bifrost_system_staking::migrations::v1::MigrateToV1, ); } diff --git a/runtime/bifrost-polkadot/src/lib.rs b/runtime/bifrost-polkadot/src/lib.rs index 9bded4470..4f2431d13 100644 --- a/runtime/bifrost-polkadot/src/lib.rs +++ b/runtime/bifrost-polkadot/src/lib.rs @@ -1835,6 +1835,7 @@ pub mod migrations { bifrost_asset_registry::migrations::v1::MigrateToV1, bifrost_slpx::migration::v2::MigrateToV2, frame_support::migrations::RemovePallet, + bifrost_system_staking::migrations::v1::MigrateToV1, ); }