From e33a964dc1551fbde1858643c70325e5124e2f88 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Sun, 28 Nov 2021 17:10:16 +0800 Subject: [PATCH 01/27] refactor homa(WIP) --- Cargo.lock | 31 +- modules/homa/Cargo.toml | 45 +- modules/homa/src/lib.rs | 982 +++++++++++++++++++-- modules/homa/src/weights.rs | 115 --- modules/relaychain/src/lib.rs | 19 +- modules/support/src/lib.rs | 10 + node/service/src/chain_spec/mandala.rs | 25 +- runtime/karura/src/lib.rs | 4 +- runtime/mandala/Cargo.toml | 11 - runtime/mandala/src/lib.rs | 86 +- runtime/mandala/src/weights/mod.rs | 1 - runtime/mandala/src/weights/module_homa.rs | 75 -- 12 files changed, 997 insertions(+), 407 deletions(-) delete mode 100644 modules/homa/src/weights.rs delete mode 100644 runtime/mandala/src/weights/module_homa.rs diff --git a/Cargo.lock b/Cargo.lock index 0691b7dc96..f542f7a740 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4884,19 +4884,15 @@ dependencies = [ "module-evm-utiltity", "module-homa", "module-homa-lite", - "module-homa-validator-list", "module-honzon", "module-idle-scheduler", "module-incentives", "module-loans", "module-nft", "module-nominees-election", - "module-polkadot-bridge", "module-prices", "module-relaychain", "module-session-manager", - "module-staking-pool", - "module-staking-pool-rpc-runtime-api", "module-support", "module-transaction-pause", "module-transaction-payment", @@ -5507,14 +5503,27 @@ name = "module-homa" version = "2.0.1" dependencies = [ "acala-primitives", + "cumulus-primitives-core", + "frame-benchmarking", "frame-support", "frame-system", + "module-currencies", + "module-relaychain", "module-support", + "orml-tokens", + "orml-traits", + "pallet-balances", + "pallet-staking", + "pallet-xcm", "parity-scale-codec", "scale-info", - "serde", + "sp-arithmetic", + "sp-core", + "sp-io", "sp-runtime", "sp-std", + "xcm", + "xcm-executor", ] [[package]] @@ -5791,18 +5800,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "module-staking-pool-rpc-runtime-api" -version = "2.0.1" -dependencies = [ - "module-support", - "parity-scale-codec", - "serde", - "sp-api", - "sp-runtime", - "sp-std", -] - [[package]] name = "module-support" version = "2.0.1" diff --git a/modules/homa/Cargo.toml b/modules/homa/Cargo.toml index 356dfeec3a..bd9ddbb940 100644 --- a/modules/homa/Cargo.toml +++ b/modules/homa/Cargo.toml @@ -5,27 +5,54 @@ authors = ["Acala Developers"] edition = "2018" [dependencies] -serde = { version = "1.0.124", optional = true } -codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } scale-info = { version = "1.0", default-features = false, features = ["derive"] } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false, optional = true} frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } -support = { package = "module-support", path = "../support", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } primitives = { package = "acala-primitives", path = "../../primitives", default-features = false } +orml-traits = { path = "../../orml/traits", default-features = false } +module-support = { path = "../../modules/support", default-features = false } + +[dev-dependencies] +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } +module-currencies = { path = "../../modules/currencies" } +orml-tokens = { path = "../../orml/tokens" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12" } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12" } +module-relaychain = { path = "../relaychain", features = ["kusama"] } [features] default = ["std"] std = [ - "serde", "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", "scale-info/std", + "sp-arithmetic/std", "sp-runtime/std", + "sp-core/std", "sp-std/std", - "frame-support/std", - "frame-system/std", - "support/std", + "pallet-staking/std", + "pallet-xcm/std", + "xcm/std", "primitives/std", + "orml-traits/std", + "module-support/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", ] try-runtime = ["frame-support/try-runtime"] diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 8e763db8c3..d406ce5ba1 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -16,118 +16,962 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! # Homa Module -//! -//! ## Overview -//! -//! The user entrance of Homa protocol. User can inject DOT into the staking -//! pool and get LDOT, which is the redemption voucher for DOT owned by the -//! staking pool. The staking pool will staking these DOT to get staking -//! rewards. Holders of LDOT can choose different ways to redeem DOT. - #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::unused_unit)] -use codec::MaxEncodedLen; -use frame_support::{pallet_prelude::*, transactional}; -use frame_system::pallet_prelude::*; -use primitives::{Balance, EraIndex}; +use frame_support::{log, pallet_prelude::*, transactional, weights::Weight, BoundedVec, PalletId}; +use frame_system::{ensure_signed, pallet_prelude::*}; +use module_support::{CallBuilder, ExchangeRate, ExchangeRateProvider, Rate}; +use orml_traits::{ + arithmetic::Signed, BalanceStatus, MultiCurrency, MultiCurrencyExtended, MultiReservableCurrency, XcmTransfer, +}; +use pallet_staking::EraIndex; +use primitives::{Balance, CurrencyId}; use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; -use support::HomaProtocol; - -pub mod weights; +use sp_arithmetic::traits::CheckedRem; +use sp_runtime::{ + traits::{AccountIdConversion, BlockNumberProvider, Bounded, Convert, One, Saturating, Zero}, + ArithmeticError, FixedPointNumber, Permill, +}; +use sp_std::{ + cmp::{min, Ordering}, + convert::{From, TryFrom, TryInto}, + ops::Mul, + prelude::*, + vec::Vec, +}; +use xcm::latest::prelude::*; pub use module::*; -pub use weights::WeightInfo; - -/// Redemption modes: -/// 1. Immediately: User will immediately get back DOT from the free pool, -/// which is a liquid pool operated by staking pool, but they have to pay -/// extra fee. -/// 2. Target: User can claim the unclaimed unbonding DOT of -/// specific era, after the remaining unbinding period has passed, users can -/// get back the DOT. -/// 3. WaitFor Unbonding: User request unbond, the staking -/// pool will process unbonding in the next era, and user needs to wait for -/// the complete unbonding era which determined by Polkadot. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, MaxEncodedLen, TypeInfo)] -pub enum RedeemStrategy { - Immediately, - Target(EraIndex), - WaitForUnbonding, -} #[frame_support::pallet] pub mod module { use super::*; + /// The subaccount's staking ledger which kept by Homa protocol + #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default)] + pub struct StakingLedger { + /// Corresponding to the active of the subaccount's staking ledger on relaychain + #[codec(compact)] + pub bonded: Balance, + /// Corresponding to the unlocking of the subaccount's staking ledger on relaychain + pub unlocking: Vec, + } + + /// Just a Balance/BlockNumber tuple to encode when a chunk of funds will be unlocked. + #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] + pub struct UnlockChunk { + /// Amount of funds to be unlocked. + #[codec(compact)] + value: Balance, + /// Era number at which point it'll be unlocked. + #[codec(compact)] + era: EraIndex, + } + + impl StakingLedger { + /// Remove entries from `unlocking` that are sufficiently old and the sum of expired + /// unlocking. + fn consolidate_unlocked(self, current_era: EraIndex) -> (Self, Balance) { + let mut expired_unlocking: Balance = Zero::zero(); + let unlocking = self + .unlocking + .into_iter() + .filter(|chunk| { + if chunk.era > current_era { + true + } else { + expired_unlocking = expired_unlocking.saturating_add(chunk.value); + false + } + }) + .collect(); + + ( + Self { + bonded: self.bonded, + unlocking, + }, + expired_unlocking, + ) + } + } + + pub type SubAccountIndex = u16; + pub(crate) type AmountOf = + <::Currency as MultiCurrencyExtended<::AccountId>>::Amount; + #[pallet::config] - pub trait Config: frame_system::Config { - /// The core of Homa protocol. - type Homa: HomaProtocol; + pub trait Config: frame_system::Config + pallet_xcm::Config { + type Event: From> + IsType<::Event>; + + /// Multi-currency support for asset management + type Currency: MultiReservableCurrency + + MultiCurrencyExtended; + + /// Origin represented Governance + type GovernanceOrigin: EnsureOrigin<::Origin>; + + /// The currency id of the Staking asset + #[pallet::constant] + type StakingCurrencyId: Get; + + /// The currency id of the Liquid asset + #[pallet::constant] + type LiquidCurrencyId: Get; + + /// The homa's module id. + #[pallet::constant] + type PalletId: Get; + + /// The default exchange rate for liquid currency to staking currency. + #[pallet::constant] + type DefaultExchangeRate: Get; + + /// The threshold for mint operation in staking currency. + #[pallet::constant] + type MintThreshold: Get; + + /// The threshold for redeem operation in liquid currency. + #[pallet::constant] + type RedeemThreshold: Get; + + /// The account of parachain on the relaychain. + #[pallet::constant] + type ParachainAccount: Get; + + /// The index list of active Homa subaccounts. + /// `active` means these subaccounts can continue do bond/unbond operations by Homa. + #[pallet::constant] + type ActiveSubAccountsIndexList: Get>; + + /// The bonded soft cap for each subaccount, use len(ActiveSubAccountsIndexList) * + /// SoftBondedCapPerSubAccount as the staking currency cap. + #[pallet::constant] + type SoftBondedCapPerSubAccount: Get; + + /// The keepers list which are allowed to do fast match redeem request. + #[pallet::constant] + type FastMatchKeepers: Get>; - /// Weight information for the extrinsics in this module. - type WeightInfo: WeightInfo; + /// Number of eras for unbonding is expired on relaychain. + #[pallet::constant] + type BondingDuration: Get; + + /// Unbonding slashing spans for unbonding on the relaychain. + #[pallet::constant] + type RelayChainUnbondingSlashingSpans: Get; + + /// The estimated staking reward rate per era on relaychain. + #[pallet::constant] + type EstimatedRewardRatePerEra: Get; + + /// The fixed staking currency cost of transaction fee for XCMTransfer. + #[pallet::constant] + type XcmTransferFee: Get; + + /// The fixed staking currency cost of extra fee for xcm message + #[pallet::constant] + type XcmMessageFee: Get; + + /// The Call builder for communicating with RelayChain via XCM messaging. + type RelayChainCallBuilder: CallBuilder; + + /// The interface to Cross-chain transfer. + type XcmTransfer: XcmTransfer; + + /// The convert for convert sovereign subacocunt index to the MultiLocation where the + /// staking currencies are sent to. + type SovereignSubAccountLocationConvert: Convert; + } + + #[pallet::error] + pub enum Error { + /// The mint amount is below the threshold. + BelowMintThreshold, + /// The redeem amount to request is below the threshold. + BelowRedeemThreshold, + /// The caller is not in `FastMatchKeepers` list. + NotAllowedKeeper, + /// The mint will cause staking currency of Homa exceed the soft cap. + ExceededStakingCurrencySoftCap, + /// UnclaimedRedemption is not enough, this error is not expected. + InsufficientUnclaimedRedemption, + /// Invalid era index to bump, must be greater than RelayChainCurrentEra + InvalidEraIndex, + /// The xcm operation have failed + XcmFailed, } + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// The minter use staking currency to mint liquid currency. \[minter, + /// staking_currency_amount, liquid_currency_amount_received\] + Minted(T::AccountId, Balance, Balance), + /// Request redeem. \[redeemer, liquid_amount, fast_match_fee_rate\] + RequestedRedeem(T::AccountId, Balance, Rate), + /// Redeem request has been cancelled. \[redeemer, cancelled_liquid_amount\] + RedeemRequestCancelled(T::AccountId, Balance), + /// Redeem request is redeemed partially or fully by fast match. \[redeemer, + /// matched_liquid_amount, fee_in_liquid, redeemed_staking_amount\] + RedeemedByFastMatch(T::AccountId, Balance, Balance, Balance), + /// The redeemer withdraw expired redemption. \[redeemer, redeption_amount\] + WithdrawRedemption(T::AccountId, Balance), + /// The redeemer withdraw expired redemption. \[redeemer, redeption_amount\] + XcmDestWeightUpdated(Weight), + /// The current era has been bumped. \[new_era_index\] + CurrentEraBumped(EraIndex), + /// The bonded amount of subaccount's ledger has been updated. \[sub_account_index, + /// new_bonded_amount\] + LedgerBondedUpdated(SubAccountIndex, Balance), + /// The unlocking of subaccount's ledger has been updated. \[sub_account_index, + /// new_unlocking\] + LedgerUnlockingUpdated(SubAccountIndex, Vec), + } + + /// The current era of relaychain + /// + /// RelayChainCurrentEra : EraIndex + #[pallet::storage] + #[pallet::getter(fn relay_chain_current_era)] + pub type RelayChainCurrentEra = StorageValue<_, EraIndex, ValueQuery>; + + // /// The latest processed era on by, it should be always <= RelayChainCurrentEra + // /// + // /// ProcessedEra : EraIndex + // #[pallet::storage] + // #[pallet::getter(fn processed_era)] + // pub type ProcessedEra = StorageValue<_, EraIndex, ValueQuery>; + + /// The staking ledger of Homa subaccounts. + /// + /// StakingLedgers map: SubAccountIndex => Option + #[pallet::storage] + #[pallet::getter(fn staking_ledgers)] + pub type StakingLedgers = StorageMap<_, Twox64Concat, SubAccountIndex, StakingLedger, OptionQuery>; + + /// The total staking currency to bond on relaychain when new era, + /// and that is available to be match fast redeem request. + /// ToBondPool value: StakingCurrencyAmount + #[pallet::storage] + #[pallet::getter(fn to_bond_pool)] + pub type ToBondPool = StorageValue<_, Balance, ValueQuery>; + + /// The total amount of void liquid currency. It's will not be issued, + /// used to avoid newly issued LDOT to obtain the incoming staking income from relaychain. + /// And it is guaranteed that the current exchange rate between liquid currency and staking + /// currency will not change. It will be reset to 0 at the end of the rebalance when new era. + /// + /// TotalVoidLiquid value: LiquidCurrencyAmount + #[pallet::storage] + #[pallet::getter(fn total_void_liquid)] + pub type TotalVoidLiquid = StorageValue<_, Balance, ValueQuery>; + + /// The total unclaimed redemption. + /// + /// UnclaimedRedemption value: StakingCurrencyAmount + #[pallet::storage] + #[pallet::getter(fn unclaimed_redemption)] + pub type UnclaimedRedemption = StorageValue<_, Balance, ValueQuery>; + + /// Requests to redeem staked currencies. + /// RedeemRequests: Map: AccountId => Option<(liquid_amount: Balance, addtional_fee: Rate)> + #[pallet::storage] + #[pallet::getter(fn redeem_requests)] + pub type RedeemRequests = StorageMap<_, Twox64Concat, T::AccountId, (Balance, Rate), OptionQuery>; + + /// The records of unbonding by AccountId. + /// + /// Unbondings: double_map AccountId, ExpireEraIndex => UnboundingStakingCurrencyAmount + #[pallet::storage] + #[pallet::getter(fn unbondings)] + pub type Unbondings = + StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, EraIndex, Balance, ValueQuery>; + + /// The weight for excution XCM msg on relaychain. Must be higher than + /// T::BaseXcmWeight + T::Weigher::weight(xcm_message_sended_by_homa)` on relaychain. + /// + /// xcm_dest_weight: value: Weight + #[pallet::storage] + #[pallet::getter(fn xcm_dest_weight)] + pub type XcmDestWeight = StorageValue<_, Weight, ValueQuery>; + #[pallet::pallet] pub struct Pallet(_); #[pallet::hooks] - impl Hooks for Pallet {} + impl Hooks for Pallet { + fn integrity_test() { + assert!(!T::DefaultExchangeRate::get().is_zero()); + } + } #[pallet::call] impl Pallet { - /// Inject DOT to staking pool and mint LDOT in a certain exchange rate - /// decided by staking pool. + /// Mint liquid currency by put locking up amount of staking currency. /// - /// - `amount`: the DOT amount to inject into staking pool. - #[pallet::weight(::WeightInfo::mint())] + /// Parameters: + /// - `amount`: The amount of staking currency used to mint liquid currency. + #[pallet::weight(10_000)] #[transactional] pub fn mint(origin: OriginFor, #[pallet::compact] amount: Balance) -> DispatchResult { - let who = ensure_signed(origin)?; - T::Homa::mint(&who, amount)?; + let minter = ensure_signed(origin)?; + + // Ensure the amount is above the mint threshold. + ensure!(amount > T::MintThreshold::get(), Error::::BelowMintThreshold); + + // TODO: is that neccesary? + ensure!( + Self::get_total_staking_currency().saturating_add(amount) <= Self::get_staking_currency_soft_cap(), + Error::::ExceededStakingCurrencySoftCap + ); + + T::Currency::transfer(T::StakingCurrencyId::get(), &minter, &Self::account_id(), amount)?; + ToBondPool::::mutate(|pool| *pool = pool.saturating_add(amount)); + + // calculate the liquid amount by the current exchange rate. + let liquid_amount = Self::convert_staking_to_liquid(amount)?; + let liquid_issue_to_minter = Rate::one() + .saturating_add(T::EstimatedRewardRatePerEra::get()) + .reciprocal() + .expect("shouldn't be invalid!") + .saturating_mul_int(liquid_amount); + let liquid_add_to_void = liquid_amount.saturating_sub(liquid_issue_to_minter); + + T::Currency::deposit(T::LiquidCurrencyId::get(), &minter, liquid_issue_to_minter)?; + TotalVoidLiquid::::mutate(|total| *total = total.saturating_add(liquid_add_to_void)); + + Self::deposit_event(Event::::Minted(minter, amount, liquid_issue_to_minter)); Ok(()) } - /// Burn LDOT and redeem DOT from staking pool. + /// Build/Cancel/Overwrite a redeem request, use liquid currency to redeem staking currency. + /// The redeem request will be executed in two ways: + /// 1. Redeem by fast match: Homa use staking currency in ToBondPool to match redeem request + /// in the current era, setting a higher fee_rate can increase the possibility of being fast + /// matched. 2. Redeem by unbond on relaychain: if redeem request has not been fast matched + /// in current era, Homa will unbond staking currency on relaychain when the next era + /// bumped. So redeemer at least wait for the unbonding period + extra 1 era to get the + /// redemption. /// - /// - `amount`: the LDOT amount to redeem. - /// - `strategy`: redemption mode. - #[pallet::weight(match *strategy { - RedeemStrategy::Immediately => ::WeightInfo::redeem_immediately(), - RedeemStrategy::Target(_) => ::WeightInfo::redeem_by_claim_unbonding(), - RedeemStrategy::WaitForUnbonding => ::WeightInfo::redeem_wait_for_unbonding(), - })] + /// Parameters: + /// - `amount`: The amount of liquid currency to be requested redeemed into Staking + /// currency. + /// - `fast_match_fee_rate`: Fee rate for pay fast match. + #[pallet::weight(10_000)] #[transactional] - pub fn redeem( + pub fn request_redeem( origin: OriginFor, #[pallet::compact] amount: Balance, - strategy: RedeemStrategy, + fast_match_fee_rate: Rate, ) -> DispatchResult { + let redeemer = ensure_signed(origin)?; + + RedeemRequests::::try_mutate_exists(&redeemer, |maybe_request| -> DispatchResult { + let (previous_request_amount, _) = maybe_request.take().unwrap_or_default(); + let liquid_currency_id = T::LiquidCurrencyId::get(); + + ensure!( + (!previous_request_amount.is_zero() && amount.is_zero()) || amount >= T::RedeemThreshold::get(), + Error::::BelowRedeemThreshold + ); + + match amount.cmp(&previous_request_amount) { + Ordering::Greater => + // pay more liquid currency. + { + T::Currency::transfer( + liquid_currency_id, + &redeemer, + &Self::account_id(), + amount.saturating_sub(previous_request_amount), + ) + } + Ordering::Less => + // refund the difference. + { + T::Currency::transfer( + liquid_currency_id, + &Self::account_id(), + &redeemer, + previous_request_amount.saturating_sub(amount), + ) + } + _ => Ok(()), + }?; + + if !amount.is_zero() { + *maybe_request = Some((amount, fast_match_fee_rate)); + Self::deposit_event(Event::::RequestedRedeem( + redeemer.clone(), + amount, + fast_match_fee_rate, + )); + } else { + if !previous_request_amount.is_zero() { + Self::deposit_event(Event::::RedeemRequestCancelled( + redeemer.clone(), + previous_request_amount, + )); + } + } + Ok(()) + }) + } + + /// Execute fast match for specific redeem requests. + /// Caller must be in `FastMatchKeepers` list. + /// + /// Parameters: + /// - `redeemer_list`: The list of redeem requests to execute fast redeem. + #[pallet::weight(10_000)] + #[transactional] + pub fn fast_match_redeems(origin: OriginFor, redeemer_list: Vec) -> DispatchResult { let who = ensure_signed(origin)?; - match strategy { - RedeemStrategy::Immediately => { - T::Homa::redeem_by_free_unbonded(&who, amount)?; + ensure!(T::FastMatchKeepers::get().contains(&who), Error::::NotAllowedKeeper); + + for redeemer in redeemer_list { + Self::do_fast_match_redeem(&redeemer)?; + } + Ok(()) + } + + /// Withdraw the expired redemption of specific redeemer by unbond. + /// + /// Parameters: + /// - `redeemer`: redeemer. + #[pallet::weight(10_000)] + #[transactional] + pub fn claim_redemption(origin: OriginFor, redeemer: T::AccountId) -> DispatchResult { + let _ = ensure_signed(origin)?; + + let mut available_staking: Balance = Zero::zero(); + Unbondings::::iter_prefix(&redeemer) + .filter(|(era_index, _)| era_index <= &Self::relay_chain_current_era()) + .for_each(|(expired_era_index, unbonded)| { + available_staking = available_staking.saturating_add(available_staking); + Unbondings::::remove(&redeemer, expired_era_index); + }); + UnclaimedRedemption::::try_mutate(|total| -> DispatchResult { + *total = total + .checked_sub(available_staking) + .ok_or(Error::::InsufficientUnclaimedRedemption)?; + Ok(()) + }); + T::Currency::transfer( + T::StakingCurrencyId::get(), + &Self::account_id(), + &redeemer, + available_staking, + )?; + + Self::deposit_event(Event::::WithdrawRedemption(redeemer, available_staking)); + Ok(()) + } + + /// Bump the current era to keep consistent with relaychain. + /// Requires `GovernanceOrigin` + /// + /// Parameters: + /// - `new_era`: the latest era index of relaychain. + #[pallet::weight(10_000)] + #[transactional] + pub fn bump_current_era(origin: OriginFor, new_era: EraIndex) -> DispatchResult { + T::GovernanceOrigin::ensure_origin(origin)?; + + RelayChainCurrentEra::::try_mutate(|current_era| -> DispatchResult { + ensure!(new_era > *current_era, Error::::InvalidEraIndex); + *current_era = new_era; + + // reset void liquid to zero + TotalVoidLiquid::::put(0); + + // TODO: consider execute rebalance on on_idle, tut before the processing is completed, + // the mint and request_redeem functions should be unavailable. + // Rebalance: + Self::process_scheduled_unbond(new_era)?; + Self::process_to_bond_pool(new_era)?; + Self::process_redeem_requests(new_era)?; + + Self::deposit_event(Event::::CurrentEraBumped(new_era)); + Ok(()) + }) + } + + /// Update the bonded and unbonding to local subaccounts ledger according to the ledger on + /// relaychain. Requires `GovernanceOrigin` + /// + /// Parameters: + /// - `new_era`: the latest era index of relaychain. + #[pallet::weight(10_000)] + #[transactional] + pub fn update_ledgers( + origin: OriginFor, + updates: Vec<(SubAccountIndex, Option, Option>)>, + ) -> DispatchResult { + T::GovernanceOrigin::ensure_origin(origin)?; + + for (sub_account_index, bonded_change, unlocking_change) in updates { + Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { + if let Some(bonded) = bonded_change { + ledger.bonded = bonded; + } + if let Some(unlocking) = unlocking_change { + ledger.unlocking = unlocking; + } + Ok(()) + })?; + } + + Ok(()) + } + + /// Sets the xcm_dest_weight for XCM staking operations. + /// Requires `GovernanceOrigin` + /// + /// Parameters: + /// - `xcm_dest_weight`: The new weight for XCM staking operations. + #[pallet::weight(10_000)] + #[transactional] + pub fn update_xcm_dest_weight( + origin: OriginFor, + #[pallet::compact] xcm_dest_weight: Weight, + ) -> DispatchResult { + T::GovernanceOrigin::ensure_origin(origin)?; + + XcmDestWeight::::put(xcm_dest_weight); + Self::deposit_event(Event::::XcmDestWeightUpdated(xcm_dest_weight)); + Ok(()) + } + } + + impl Pallet { + /// Module account id + pub fn account_id() -> T::AccountId { + T::PalletId::get().into_account() + } + + fn do_update_ledger( + sub_account_index: SubAccountIndex, + f: impl FnOnce(&mut StakingLedger) -> sp_std::result::Result, + ) -> sp_std::result::Result { + StakingLedgers::::try_mutate_exists(sub_account_index, |maybe_ledger| { + let mut ledger = maybe_ledger.take().unwrap_or_default(); + let old_ledger = ledger.clone(); + + f(&mut ledger).map(move |result| { + if ledger.bonded != old_ledger.bonded { + Self::deposit_event(Event::::LedgerBondedUpdated(sub_account_index, ledger.bonded)); + } + if ledger.unlocking != old_ledger.unlocking { + Self::deposit_event(Event::::LedgerUnlockingUpdated( + sub_account_index, + ledger.unlocking.clone(), + )); + } + + *maybe_ledger = if ledger == Default::default() { + None + } else { + Some(ledger) + }; + + result + }) + }) + } + + /// Get the soft cap of total staking currency of Homa. + /// Soft cap = ActiveSubAccountsIndexList.len() * SoftBondedCapPerSubAccount + pub fn get_staking_currency_soft_cap() -> Balance { + T::SoftBondedCapPerSubAccount::get().saturating_mul(T::ActiveSubAccountsIndexList::get().len() as Balance) + } + + /// Calculate the total amount of staking currency belong to Homa. + pub fn get_total_staking_currency() -> Balance { + let total_bonded: Balance = StakingLedgers::::iter().fold(Zero::zero(), |total_bonded, (_, ledger)| { + total_bonded.saturating_add(ledger.bonded) + }); + let to_bond_pool = Self::to_bond_pool(); + total_bonded.saturating_add(to_bond_pool) + } + + /// Calculate the current exchange rate between the staking currency and liquid currency. + /// Note: ExchangeRate(staking : liquid) = total_staking_amount / (liquid_total_issuance + + /// total_void_liquid) If the exchange rate cannot be calculated, T::DefaultExchangeRate is + /// used. + pub fn current_exchange_rate() -> ExchangeRate { + let total_staking = Self::get_total_staking_currency(); + let total_liquid = + T::Currency::total_issuance(T::LiquidCurrencyId::get()).saturating_add(Self::total_void_liquid()); + if total_staking.is_zero() { + T::DefaultExchangeRate::get() + } else { + ExchangeRate::checked_from_rational(total_staking, total_liquid) + .unwrap_or_else(T::DefaultExchangeRate::get) + } + } + + /// Calculate the amount of staking currency converted from liquid currency by current + /// exchange rate. + pub fn convert_liquid_to_staking(liquid_amount: Balance) -> Result { + Self::current_exchange_rate() + .checked_mul_int(liquid_amount) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow)) + } + + /// Calculate the amount of liquid currency converted from staking currency by current + /// exchange rate. + pub fn convert_staking_to_liquid(staking_amount: Balance) -> Result { + Self::current_exchange_rate() + .reciprocal() + .unwrap_or_else(|| T::DefaultExchangeRate::get().reciprocal().unwrap()) + .checked_mul_int(staking_amount) + .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow)) + } + + #[transactional] + pub fn do_fast_match_redeem(redeemer: &T::AccountId) -> DispatchResult { + RedeemRequests::::try_mutate_exists(redeemer, |maybe_request| -> DispatchResult { + if let Some((request_amount, fee_rate)) = maybe_request.take() { + // calculate the liquid currency limit can be used to redeem based on ToBondPool at fee_rate. + let available_staking_currency = Self::to_bond_pool(); + let liquid_currency_limit = Self::convert_staking_to_liquid(available_staking_currency)?; + let liquid_limit_at_fee_rate = Rate::one() + .saturating_sub(fee_rate) + .reciprocal() + .unwrap_or_else(Bounded::max_value) + .saturating_mul_int(liquid_currency_limit); + let module_account = Self::account_id(); + + // calculate the acutal liquid currency to be used to redeem + let actual_liquid_to_redeem = if liquid_limit_at_fee_rate >= request_amount { + request_amount + } else { + // if cannot fast match the request amount fully, at least keep RedeemThreshold as remainer. + liquid_limit_at_fee_rate.min(request_amount.saturating_sub(T::RedeemThreshold::get())) + }; + + if !actual_liquid_to_redeem.is_zero() { + let liquid_to_burn = Rate::one() + .saturating_sub(fee_rate) + .saturating_mul_int(actual_liquid_to_redeem); + let redeemed_staking = Self::convert_liquid_to_staking(liquid_to_burn)?; + let fee_in_liquid = actual_liquid_to_redeem.saturating_sub(liquid_to_burn); + + // TODO: record the fee_in_liquid reward it to HomaTreasury as benifit. + + // burn liquid_to_burn + T::Currency::withdraw(T::LiquidCurrencyId::get(), &module_account, liquid_to_burn)?; + + // transfer redeemed_staking to redeemer. + T::Currency::transfer( + T::StakingCurrencyId::get(), + &module_account, + redeemer, + redeemed_staking, + )?; + ToBondPool::::mutate(|pool| *pool = pool.saturating_sub(redeemed_staking)); + + Self::deposit_event(Event::::RedeemedByFastMatch( + redeemer.clone(), + actual_liquid_to_redeem, + fee_in_liquid, + redeemed_staking, + )); + } + + // update request amount + let remainer_request_amount = request_amount.saturating_sub(actual_liquid_to_redeem); + if !remainer_request_amount.is_zero() { + *maybe_request = Some((remainer_request_amount, fee_rate)); + } } - RedeemStrategy::Target(target_era) => { - T::Homa::redeem_by_claim_unbonding(&who, amount, target_era)?; + + Ok(()) + }) + } + + /// Get back unbonded of all subaccounts on relaychain by XCM. + /// The staking currency withdrew becomes available to be redeemed. + #[transactional] + pub fn process_scheduled_unbond(new_era: EraIndex) -> DispatchResult { + log::debug!( + target: "homa", + "process scheduled unbond on era: {:?}", + new_era + ); + + let mut total_withdrawn_staking: Balance = Zero::zero(); + + // iterate all subaccounts + for (sub_account_index, ledger) in StakingLedgers::::iter() { + let (new_ledger, expired_unlocking) = ledger.consolidate_unlocked(new_era); + + if !expired_unlocking.is_zero() { + Self::withdraw_unbonded_from_relaychain(sub_account_index, expired_unlocking)?; + + // udpate ledger + Self::do_update_ledger(sub_account_index, |before| -> DispatchResult { + *before = new_ledger; + Ok(()) + })?; + total_withdrawn_staking = total_withdrawn_staking.saturating_add(expired_unlocking); } - RedeemStrategy::WaitForUnbonding => { - T::Homa::redeem_by_unbond(&who, amount)?; + } + + // issue withdrawn unbonded to module account for redeemer to claim + T::Currency::deposit( + T::StakingCurrencyId::get(), + &Self::account_id(), + total_withdrawn_staking, + )?; + UnclaimedRedemption::::mutate(|total| *total = total.saturating_add(total_withdrawn_staking)); + + Ok(()) + } + + /// Distribute PoolToBond to ActiveSubAccountsIndexList, then cross-transfer the + /// distribution amount to the subaccounts on relaychain and bond it by XCM. + #[transactional] + pub fn process_to_bond_pool(new_era: EraIndex) -> DispatchResult { + log::debug!( + target: "homa", + "process to bond pool on era: {:?}", + new_era + ); + + let xcm_transfer_fee = T::XcmTransferFee::get(); + let bonded_list: Vec<(SubAccountIndex, Balance)> = T::ActiveSubAccountsIndexList::get() + .iter() + .map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded)) + .collect(); + let (distribution, remainer) = distribute_increment::( + bonded_list, + Self::to_bond_pool(), + Some(T::SoftBondedCapPerSubAccount::get()), + Some(xcm_transfer_fee), + ); + + // subaccounts execute the distribution + for (sub_account_index, amount) in distribution { + if !amount.is_zero() { + Self::transfer_and_bond_to_relaychain(sub_account_index, amount)?; + + // udpate ledger + Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { + ledger.bonded = ledger.bonded.saturating_add(amount.saturating_sub(xcm_transfer_fee)); + Ok(()) + })?; } } + + // update pool + ToBondPool::::mutate(|pool| *pool = remainer); Ok(()) } - /// Get back those DOT that have been unbonded. - #[pallet::weight(::WeightInfo::withdraw_redemption())] + /// Process redeem requests and subaccounts do unbond on relaychain by XCM message. #[transactional] - pub fn withdraw_redemption(origin: OriginFor) -> DispatchResult { - let who = ensure_signed(origin)?; - T::Homa::withdraw_redemption(&who)?; + pub fn process_redeem_requests(new_era: EraIndex) -> DispatchResult { + log::debug!( + target: "homa", + "process redeem requests on era: {:?}", + new_era + ); + + let mut total_redeem_amount: Balance = Zero::zero(); + let era_index_to_expire = new_era + T::BondingDuration::get(); + + // drain RedeemRequests and insert to Unbondings + for (redeemer, (redeem_amount, _)) in RedeemRequests::::drain() { + total_redeem_amount = total_redeem_amount.saturating_add(redeem_amount); + let redemption_amount = Self::convert_liquid_to_staking(redeem_amount)?; + Unbondings::::insert(redeemer, era_index_to_expire, redemption_amount); + } + + // calculate the distribution for unbond + let staking_amount_to_unbond = Self::convert_liquid_to_staking(total_redeem_amount)?; + let bonded_list: Vec<(SubAccountIndex, Balance)> = T::ActiveSubAccountsIndexList::get() + .iter() + .map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded)) + .collect(); + let (distribution, _) = + distribute_decrement::(bonded_list, staking_amount_to_unbond, None, None); + + // subaccounts execute the distribution + for (sub_account_index, unbond_amount) in distribution { + if !unbond_amount.is_zero() { + Self::unbond_on_relaychain(sub_account_index, unbond_amount)?; + + // udpate ledger + Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { + ledger.bonded = ledger.bonded.saturating_sub(unbond_amount); + ledger.unlocking.push(UnlockChunk { + value: unbond_amount, + era: era_index_to_expire, + }); + Ok(()) + })?; + } + } + + // burn total_redeem_amount. + T::Currency::withdraw(T::LiquidCurrencyId::get(), &Self::account_id(), total_redeem_amount)?; + + Ok(()) + } + + /// Send XCM message to the relaychain to withdraw_unbonded staking currency from + /// subaccount. + pub fn withdraw_unbonded_from_relaychain( + sub_account_index: SubAccountIndex, + amount: Balance, + ) -> DispatchResult { + let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( + T::RelayChainCallBuilder::utility_as_derivative_call( + T::RelayChainCallBuilder::utility_batch_call(vec![ + T::RelayChainCallBuilder::staking_withdraw_unbonded(T::RelayChainUnbondingSlashingSpans::get()), + T::RelayChainCallBuilder::balances_transfer_keep_alive(T::ParachainAccount::get(), amount), + ]), + sub_account_index, + ), + T::XcmMessageFee::get(), + Self::xcm_dest_weight(), + ); + + let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); + log::debug!( + target: "homa", + "subaccount {:?} send XCM to withdraw unbonded {:?} on relaychain result: {:?}", + sub_account_index, amount, result + ); + ensure!(result.is_ok(), Error::::XcmFailed); + Ok(()) + } + + /// Cross-chain transfer staking currency to subaccount and bond it on relaychain by XCM + /// message. + pub fn transfer_and_bond_to_relaychain(sub_account_index: SubAccountIndex, amount: Balance) -> DispatchResult { + T::XcmTransfer::transfer( + Self::account_id(), + T::StakingCurrencyId::get(), + amount, + T::SovereignSubAccountLocationConvert::convert(sub_account_index), + Self::xcm_dest_weight(), + )?; + + let bond_amount = amount.saturating_sub(T::XcmTransferFee::get()); + let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( + T::RelayChainCallBuilder::utility_as_derivative_call( + T::RelayChainCallBuilder::staking_bond_extra(bond_amount), + sub_account_index, + ), + T::XcmMessageFee::get(), + Self::xcm_dest_weight(), + ); + let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); + log::debug!( + target: "homa", + "subaccount {:?} send XCM to bond {:?} on relaychain result: {:?}", + sub_account_index, bond_amount, result, + ); + ensure!(result.is_ok(), Error::::XcmFailed); Ok(()) } + + /// Send XCM message to the relaychain to unbond subaccount. + pub fn unbond_on_relaychain(sub_account_index: SubAccountIndex, amount: Balance) -> DispatchResult { + let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( + T::RelayChainCallBuilder::utility_as_derivative_call( + T::RelayChainCallBuilder::staking_unbond(amount), + sub_account_index, + ), + T::XcmMessageFee::get(), + Self::xcm_dest_weight(), + ); + let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); + log::debug!( + target: "homa", + "subaccount {:?} send XCM to unbond {:?} on relaychain result: {:?}", + sub_account_index, amount, result + ); + ensure!(result.is_ok(), Error::::XcmFailed); + Ok(()) + } + } + + impl ExchangeRateProvider for Pallet { + fn get_exchange_rate() -> ExchangeRate { + Self::current_exchange_rate() + } } } + +/// Helpers for distribute increment/decrement to as possible to keep the list balanced after +/// distribution. +pub fn distribute_increment( + mut amount_list: Vec<(Index, Balance)>, + total_increment: Balance, + amount_cap: Option, + minimum_increment: Option, +) -> (Vec<(Index, Balance)>, Balance) { + let mut remain_increment = total_increment; + let mut distribution_list: Vec<(Index, Balance)> = vec![]; + + // Sort by amount in ascending order + amount_list.sort_by(|a, b| a.1.cmp(&b.1)); + + for (index, amount) in amount_list { + if remain_increment.is_zero() || remain_increment < minimum_increment.unwrap_or_else(Bounded::max_value) { + break; + } + + let increment_distribution = amount_cap + .unwrap_or_else(Bounded::max_value) + .saturating_add(amount) + .min(remain_increment); + if increment_distribution.is_zero() + || increment_distribution < minimum_increment.unwrap_or_else(Bounded::max_value) + { + continue; + } + distribution_list.push((index, increment_distribution)); + remain_increment = remain_increment.saturating_sub(increment_distribution); + } + + (distribution_list, remain_increment) +} + +pub fn distribute_decrement( + mut amount_list: Vec<(Index, Balance)>, + total_decrement: Balance, + amount_remainer: Option, + minimum_decrement: Option, +) -> (Vec<(Index, Balance)>, Balance) { + let mut remain_decrement = total_decrement; + let mut distribution_list: Vec<(Index, Balance)> = vec![]; + + // Sort by amount in descending order + amount_list.sort_by(|a, b| b.1.cmp(&a.1)); + + for (index, amount) in amount_list { + if remain_decrement.is_zero() || remain_decrement < minimum_decrement.unwrap_or_else(Bounded::max_value) { + break; + } + + let decrement_distribution = amount + .saturating_sub(amount_remainer.unwrap_or_else(Bounded::min_value)) + .min(remain_decrement); + if decrement_distribution.is_zero() + || decrement_distribution < minimum_decrement.unwrap_or_else(Bounded::min_value) + { + continue; + } + distribution_list.push((index, decrement_distribution)); + remain_decrement = remain_decrement.saturating_sub(decrement_distribution); + } + + (distribution_list, remain_decrement) +} diff --git a/modules/homa/src/weights.rs b/modules/homa/src/weights.rs deleted file mode 100644 index 2e2ed72e25..0000000000 --- a/modules/homa/src/weights.rs +++ /dev/null @@ -1,115 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2021 Acala Foundation. -// 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 . - - -//! Autogenerated weights for module_homa -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-02-26, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 - -// Executed Command: -// target/release/acala -// benchmark -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=module_homa -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --output=./modules/homa/src/weights.rs -// --template=./templates/module-weight-template.hbs - - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(clippy::unnecessary_cast)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for module_homa. -pub trait WeightInfo { - fn mint() -> Weight; - fn redeem_immediately() -> Weight; - fn redeem_wait_for_unbonding() -> Weight; - fn redeem_by_claim_unbonding() -> Weight; - fn withdraw_redemption() -> Weight; -} - -/// Weights for module_homa using the Acala node and recommended hardware. -pub struct AcalaWeight(PhantomData); -impl WeightInfo for AcalaWeight { - fn mint() -> Weight { - (100_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(8 as Weight)) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) - } - fn redeem_immediately() -> Weight { - (115_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) - } - fn redeem_wait_for_unbonding() -> Weight { - (59_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(4 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) - } - fn redeem_by_claim_unbonding() -> Weight { - (89_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(7 as Weight)) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) - } - fn withdraw_redemption() -> Weight { - (64_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - fn mint() -> Weight { - (100_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(8 as Weight)) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) - } - fn redeem_immediately() -> Weight { - (115_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) - } - fn redeem_wait_for_unbonding() -> Weight { - (59_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(4 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) - } - fn redeem_by_claim_unbonding() -> Weight { - (89_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(7 as Weight)) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) - } - fn withdraw_redemption() -> Weight { - (64_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } -} diff --git a/modules/relaychain/src/lib.rs b/modules/relaychain/src/lib.rs index d8fecc4c2a..ce4ec27a82 100644 --- a/modules/relaychain/src/lib.rs +++ b/modules/relaychain/src/lib.rs @@ -39,7 +39,10 @@ use frame_system::Config; #[derive(Encode, Decode, RuntimeDebug)] pub enum BalancesCall { #[codec(index = 3)] - TransferKeepAlive(::Source, #[codec(compact)] Balance), + TransferKeepAlive(::Source, #[codec(compact)] Balance), /* TODO: because param type + * in relaychain is u64, + * need to confirm + * Balance(u128) is work. */ } #[derive(Encode, Decode, RuntimeDebug)] @@ -52,6 +55,12 @@ pub enum UtilityCall { #[derive(Encode, Decode, RuntimeDebug)] pub enum StakingCall { + #[codec(index = 1)] + BondExtra(#[codec(compact)] Balance), /* TODO: because param type in relaychain is u64, need to confirm + * Balance(u128) is work. */ + #[codec(index = 2)] + Unbond(#[codec(compact)] Balance), /* TODO: because param type in relaychain is u64, need to confirm + * Balance(u128) is work. */ #[codec(index = 3)] WithdrawUnbonded(u32), } @@ -115,6 +124,14 @@ where RelayChainCall::Utility(Box::new(UtilityCall::AsDerivative(index, call))) } + fn staking_bond_extra(amount: Self::Balance) -> Self::RelayChainCall { + RelayChainCall::Staking(StakingCall::BondExtra(amount)) + } + + fn staking_unbond(amount: Self::Balance) -> Self::RelayChainCall { + RelayChainCall::Staking(StakingCall::Unbond(amount)) + } + fn staking_withdraw_unbonded(num_slashing_spans: u32) -> Self::RelayChainCall { RelayChainCall::Staking(StakingCall::WithdrawUnbonded(num_slashing_spans)) } diff --git a/modules/support/src/lib.rs b/modules/support/src/lib.rs index c1de5b1b6d..806caa4bf7 100644 --- a/modules/support/src/lib.rs +++ b/modules/support/src/lib.rs @@ -597,6 +597,16 @@ pub trait CallBuilder { /// - index: The index of sub-account to be used as the new origin. fn utility_as_derivative_call(call: Self::RelayChainCall, index: u16) -> Self::RelayChainCall; + /// Bond extra on relay-chain. + /// params: + /// - amount: The amount of staking currency to bond. + fn staking_bond_extra(amount: Self::Balance) -> Self::RelayChainCall; + + /// Unbond on relay-chain. + /// params: + /// - amount: The amount of staking currency to unbond. + fn staking_unbond(amount: Self::Balance) -> Self::RelayChainCall; + /// Withdraw unbonded staking on the relay-chain. /// params: /// - num_slashing_spans: The number of slashing spans to withdraw from. diff --git a/node/service/src/chain_spec/mandala.rs b/node/service/src/chain_spec/mandala.rs index e9b348edda..269ed77738 100644 --- a/node/service/src/chain_spec/mandala.rs +++ b/node/service/src/chain_spec/mandala.rs @@ -240,7 +240,7 @@ fn testnet_genesis( DexConfig, EVMConfig, EnabledTradingPairs, FinancialCouncilMembershipConfig, GeneralCouncilMembershipConfig, HomaCouncilMembershipConfig, IndicesConfig, NativeTokenExistentialDeposit, OperatorMembershipAcalaConfig, OrmlNFTConfig, ParachainInfoConfig, PolkadotXcmConfig, RenVmBridgeConfig, SessionConfig, SessionDuration, - SessionKeys, SessionManagerConfig, StakingPoolConfig, StarportConfig, SudoConfig, SystemConfig, + SessionKeys, SessionManagerConfig, StarportConfig, SudoConfig, SystemConfig, TechnicalCommitteeMembershipConfig, TokensConfig, VestingConfig, ACA, AUSD, DOT, LDOT, RENBTC, }; @@ -362,15 +362,6 @@ fn testnet_genesis( accounts: evm_genesis_accounts, treasury: root_key, }, - staking_pool: StakingPoolConfig { - staking_pool_params: module_staking_pool::Params { - target_max_free_unbonded_ratio: FixedU128::saturating_from_rational(10, 100), - target_min_free_unbonded_ratio: FixedU128::saturating_from_rational(5, 100), - target_unbonding_to_free_ratio: FixedU128::saturating_from_rational(2, 100), - unbonding_to_free_adjustment: FixedU128::saturating_from_rational(1, 1000), - base_fee_rate: FixedU128::saturating_from_rational(2, 100), - }, - }, dex: DexConfig { initial_listing_trading_pairs: vec![], initial_enabled_trading_pairs: EnabledTradingPairs::get(), @@ -441,9 +432,8 @@ fn mandala_genesis( CollatorSelectionConfig, DexConfig, EVMConfig, EnabledTradingPairs, FinancialCouncilMembershipConfig, GeneralCouncilMembershipConfig, HomaCouncilMembershipConfig, IndicesConfig, NativeTokenExistentialDeposit, OperatorMembershipAcalaConfig, OrmlNFTConfig, ParachainInfoConfig, PolkadotXcmConfig, RenVmBridgeConfig, - SessionConfig, SessionDuration, SessionKeys, SessionManagerConfig, StakingPoolConfig, StarportConfig, - SudoConfig, SystemConfig, TechnicalCommitteeMembershipConfig, TokensConfig, VestingConfig, ACA, AUSD, DOT, - LDOT, RENBTC, + SessionConfig, SessionDuration, SessionKeys, SessionManagerConfig, StarportConfig, SudoConfig, SystemConfig, + TechnicalCommitteeMembershipConfig, TokensConfig, VestingConfig, ACA, AUSD, DOT, LDOT, RENBTC, }; let existential_deposit = NativeTokenExistentialDeposit::get(); @@ -561,15 +551,6 @@ fn mandala_genesis( accounts: evm_genesis_accounts, treasury: root_key, }, - staking_pool: StakingPoolConfig { - staking_pool_params: module_staking_pool::Params { - target_max_free_unbonded_ratio: FixedU128::saturating_from_rational(10, 100), - target_min_free_unbonded_ratio: FixedU128::saturating_from_rational(5, 100), - target_unbonding_to_free_ratio: FixedU128::saturating_from_rational(2, 100), - unbonding_to_free_adjustment: FixedU128::saturating_from_rational(1, 1000), - base_fee_rate: FixedU128::saturating_from_rational(2, 100), - }, - }, dex: DexConfig { initial_listing_trading_pairs: vec![], initial_enabled_trading_pairs: EnabledTradingPairs::get(), diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index cbd5e73547..36c778f7e4 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -1598,10 +1598,10 @@ parameter_types! { ParachainInfo::get().into_account(), RelayChainSubAccountId::HomaLite as u16 ); - pub MaxRewardPerEra: Permill = Permill::from_rational(500u32, 1_000_000u32); // 1.2 ^ (1/365) = 1.0004996359 + pub MaxRewardPerEra: Permill = Permill::from_rational(500u32, 1_000_000u32); // 1.2 ^ (1/365) - 1 ≈ 0.05% pub MintFee: Balance = 20 * millicent(KSM); // 2x XCM fee on Kusama pub DefaultExchangeRate: ExchangeRate = ExchangeRate::saturating_from_rational(1, 10); - pub BaseWithdrawFee: Permill = Permill::from_rational(35u32, 10_000u32); // 20% yield per year, unbonding period = 7 days. 1.2^(7 / 365) = 1.00350 + pub BaseWithdrawFee: Permill = Permill::from_rational(35u32, 10_000u32); // 20% yield per year, unbonding period = 7 days. 1.2^(7 / 365) - 1 ≈ 0.35% pub MaximumRedeemRequestMatchesForMint: u32 = 20; pub RelayChainUnbondingSlashingSpans: u32 = 5; pub MaxScheduledUnbonds: u32 = 14; diff --git a/runtime/mandala/Cargo.toml b/runtime/mandala/Cargo.toml index 063430a6e4..b2ee4e85f5 100644 --- a/runtime/mandala/Cargo.toml +++ b/runtime/mandala/Cargo.toml @@ -113,12 +113,8 @@ module-incentives = { path = "../../modules/incentives", default-features = fals module-support = { path = "../../modules/support", default-features = false } module-homa = { path = "../../modules/homa", default-features = false } module-homa-lite = { path = "../../modules/homa-lite", default-features = false } -module-homa-validator-list = { path = "../../modules/homa-validator-list", default-features = false } module-nominees-election = { path = "../../modules/nominees-election", default-features = false } module-session-manager = { path = "../../modules/session-manager", default-features = false } -module-staking-pool = { path = "../../modules/staking-pool", default-features = false } -module-staking-pool-rpc-runtime-api = { path = "../../modules/staking-pool/rpc/runtime-api", default-features = false } -module-polkadot-bridge = { path = "../../modules/polkadot-bridge", default-features = false } module-relaychain = { path = "../../modules/relaychain", default-features = false, features = ["polkadot"]} module-idle-scheduler = { path = "../../modules/idle-scheduler", default-features = false } nutsfinance-stable-asset = { version = "0.1.0", default-features = false, path = "../../ecosystem-modules/stable-asset/lib/stable-asset", package = "nutsfinance-stable-asset" } @@ -247,13 +243,9 @@ std = [ "module-prices/std", "module-incentives/std", "module-support/std", - "module-homa/std", "module-homa-lite/std", "module-nominees-election/std", "module-session-manager/std", - "module-staking-pool/std", - "module-staking-pool-rpc-runtime-api/std", - "module-polkadot-bridge/std", "module-relaychain/std", "module-idle-scheduler/std", "primitives/std", @@ -349,12 +341,9 @@ try-runtime = [ "module-nft/try-runtime", "module-prices/try-runtime", "module-incentives/try-runtime", - "module-homa/try-runtime", "module-homa-lite/try-runtime", "module-nominees-election/try-runtime", "module-session-manager/try-runtime", - "module-staking-pool/try-runtime", - "module-polkadot-bridge/try-runtime", "ecosystem-renvm-bridge/try-runtime", "ecosystem-starport/try-runtime", diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 2b2538c2d8..4ab72fc2dc 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -837,20 +837,13 @@ impl module_prices::Config for Runtime { type GetStakingCurrencyId = GetStakingCurrencyId; type GetLiquidCurrencyId = GetLiquidCurrencyId; type LockOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type LiquidStakingExchangeRateProvider = LiquidStakingExchangeRateProvider; + type LiquidStakingExchangeRateProvider = HomaLite; type DEX = Dex; type Currency = Currencies; type Erc20InfoMapping = EvmErc20InfoMapping; type WeightInfo = weights::module_prices::WeightInfo; } -pub struct LiquidStakingExchangeRateProvider; -impl module_support::ExchangeRateProvider for LiquidStakingExchangeRateProvider { - fn get_exchange_rate() -> ExchangeRate { - StakingPool::liquid_exchange_rate() - } -} - parameter_types! { pub const GetNativeCurrencyId: CurrencyId = ACA; pub const GetStableCurrencyId: CurrencyId = AUSD; @@ -1198,45 +1191,10 @@ impl module_incentives::Config for Runtime { type WeightInfo = weights::module_incentives::WeightInfo; } -parameter_types! { - pub const PolkadotBondingDuration: EraIndex = 7; - pub const EraLength: BlockNumber = DAYS; - pub const MaxUnbonding: u32 = 1000; -} - -impl module_polkadot_bridge::Config for Runtime { - type DOTCurrency = Currency; - type OnNewEra = (NomineesElection, StakingPool); - type BondingDuration = PolkadotBondingDuration; - type EraLength = EraLength; - type PolkadotAccountId = AccountId; - type MaxUnbonding = MaxUnbonding; -} - parameter_types! { pub const GetLiquidCurrencyId: CurrencyId = LDOT; pub const GetStakingCurrencyId: CurrencyId = DOT; pub DefaultExchangeRate: ExchangeRate = ExchangeRate::saturating_from_rational(10, 100); // 1 : 10 - pub PoolAccountIndexes: Vec = vec![1, 2, 3, 4]; -} - -impl module_staking_pool::Config for Runtime { - type Event = Event; - type StakingCurrencyId = GetStakingCurrencyId; - type LiquidCurrencyId = GetLiquidCurrencyId; - type DefaultExchangeRate = DefaultExchangeRate; - type PalletId = StakingPoolPalletId; - type PoolAccountIndexes = PoolAccountIndexes; - type UpdateOrigin = EnsureRootOrHalfHomaCouncil; - type FeeModel = CurveFeeModel; - type Nominees = NomineesElection; - type Bridge = PolkadotBridge; - type Currency = Currencies; -} - -impl module_homa::Config for Runtime { - type Homa = StakingPool; - type WeightInfo = weights::module_homa::WeightInfo; } pub fn create_x2_parachain_multilocation(index: u16) -> MultiLocation { @@ -1315,28 +1273,6 @@ impl module_nominees_election::Config for Runtime { type WeightInfo = weights::module_nominees_election::WeightInfo; } -parameter_types! { - pub MinGuaranteeAmount: Balance = dollar(LDOT); - pub const ValidatorInsuranceThreshold: Balance = 0; -} - -impl module_homa_validator_list::Config for Runtime { - type Event = Event; - type RelaychainAccountId = AccountId; - type LiquidTokenCurrency = Currency; - type MinBondAmount = MinGuaranteeAmount; - type BondingDuration = PolkadotBondingDuration; - type ValidatorInsuranceThreshold = ValidatorInsuranceThreshold; - type FreezeOrigin = EnsureRootOrHalfHomaCouncil; - type SlashOrigin = EnsureRootOrHalfHomaCouncil; - type OnSlash = module_staking_pool::OnSlash; - type LiquidStakingExchangeRateProvider = LiquidStakingExchangeRateProvider; - type WeightInfo = (); - type OnIncreaseGuarantee = (); - type OnDecreaseGuarantee = (); - type BlockNumberProvider = RelayChainBlockNumberProvider; -} - parameter_types! { pub CreateClassDeposit: Balance = 20 * dollar(ACA); pub CreateTokenDeposit: Balance = 2 * dollar(ACA); @@ -2099,11 +2035,7 @@ construct_runtime! { EmergencyShutdown: module_emergency_shutdown::{Pallet, Storage, Call, Event} = 125, // Homa - Homa: module_homa::{Pallet, Call} = 130, NomineesElection: module_nominees_election::{Pallet, Call, Storage, Event} = 131, - StakingPool: module_staking_pool::{Pallet, Call, Storage, Event, Config} = 132, - PolkadotBridge: module_polkadot_bridge::{Pallet, Call, Storage} = 133, - HomaValidatorListModule: module_homa_validator_list::{Pallet, Call, Storage, Event} = 134, HomaLite: module_homa_lite::{Pallet, Call, Storage, Event} = 135, // Acala Other @@ -2270,22 +2202,6 @@ impl_runtime_apis! { } } - impl module_staking_pool_rpc_runtime_api::StakingPoolApi< - Block, - AccountId, - Balance, - > for Runtime { - fn get_available_unbonded(account: AccountId) -> module_staking_pool_rpc_runtime_api::BalanceInfo { - module_staking_pool_rpc_runtime_api::BalanceInfo { - amount: StakingPool::get_available_unbonded(&account) - } - } - - fn get_liquid_staking_exchange_rate() -> ExchangeRate { - StakingPool::liquid_exchange_rate() - } - } - impl module_evm_rpc_runtime_api::EVMRuntimeRPCApi for Runtime { fn call( from: H160, diff --git a/runtime/mandala/src/weights/mod.rs b/runtime/mandala/src/weights/mod.rs index 51e957343e..b825f4fdcc 100644 --- a/runtime/mandala/src/weights/mod.rs +++ b/runtime/mandala/src/weights/mod.rs @@ -29,7 +29,6 @@ pub mod module_dex; pub mod module_emergency_shutdown; pub mod module_evm; pub mod module_evm_accounts; -pub mod module_homa; pub mod module_homa_lite; pub mod module_honzon; pub mod module_incentives; diff --git a/runtime/mandala/src/weights/module_homa.rs b/runtime/mandala/src/weights/module_homa.rs deleted file mode 100644 index 325ab33eee..0000000000 --- a/runtime/mandala/src/weights/module_homa.rs +++ /dev/null @@ -1,75 +0,0 @@ -// This file is part of Acala. - -// Copyright (C) 2020-2021 Acala Foundation. -// 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 . - -//! Autogenerated weights for module_homa -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-07-19, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 - -// Executed Command: -// target/release/acala -// benchmark -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=* -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --template=./templates/runtime-weight-template.hbs -// --output=./runtime/mandala/src/weights/ - - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; - -/// Weight functions for module_homa. -pub struct WeightInfo(PhantomData); -impl module_homa::WeightInfo for WeightInfo { - fn mint() -> Weight { - (195_776_000 as Weight) - .saturating_add(T::DbWeight::get().reads(9 as Weight)) - .saturating_add(T::DbWeight::get().writes(7 as Weight)) - } - fn redeem_immediately() -> Weight { - (197_625_000 as Weight) - .saturating_add(T::DbWeight::get().reads(8 as Weight)) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) - } - fn redeem_wait_for_unbonding() -> Weight { - (89_362_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) - } - fn redeem_by_claim_unbonding() -> Weight { - (146_082_000 as Weight) - .saturating_add(T::DbWeight::get().reads(8 as Weight)) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) - } - fn withdraw_redemption() -> Weight { - (111_068_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } -} From d44505e5e28ca3f712d8218176c97e634d96234d Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Mon, 29 Nov 2021 19:57:07 +0800 Subject: [PATCH 02/27] update --- modules/homa/src/lib.rs | 44 ++--- modules/homa/src/mock.rs | 380 ++++++++++++++++++++++++++++++++++++++ modules/homa/src/tests.rs | 19 ++ 3 files changed, 422 insertions(+), 21 deletions(-) create mode 100644 modules/homa/src/mock.rs create mode 100644 modules/homa/src/tests.rs diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index d406ce5ba1..fa6a8ee7ac 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -16,15 +16,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +//! Homa module. + #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::unused_unit)] use frame_support::{log, pallet_prelude::*, transactional, weights::Weight, BoundedVec, PalletId}; use frame_system::{ensure_signed, pallet_prelude::*}; use module_support::{CallBuilder, ExchangeRate, ExchangeRateProvider, Rate}; -use orml_traits::{ - arithmetic::Signed, BalanceStatus, MultiCurrency, MultiCurrencyExtended, MultiReservableCurrency, XcmTransfer, -}; +use orml_traits::{arithmetic::Signed, BalanceStatus, MultiCurrency, MultiCurrencyExtended, XcmTransfer}; use pallet_staking::EraIndex; use primitives::{Balance, CurrencyId}; use scale_info::TypeInfo; @@ -44,6 +44,9 @@ use xcm::latest::prelude::*; pub use module::*; +mod mock; +mod tests; + #[frame_support::pallet] pub mod module { use super::*; @@ -98,16 +101,13 @@ pub mod module { } pub type SubAccountIndex = u16; - pub(crate) type AmountOf = - <::Currency as MultiCurrencyExtended<::AccountId>>::Amount; #[pallet::config] pub trait Config: frame_system::Config + pallet_xcm::Config { type Event: From> + IsType<::Event>; /// Multi-currency support for asset management - type Currency: MultiReservableCurrency - + MultiCurrencyExtended; + type Currency: MultiCurrencyExtended; /// Origin represented Governance type GovernanceOrigin: EnsureOrigin<::Origin>; @@ -237,7 +237,7 @@ pub mod module { #[pallet::getter(fn relay_chain_current_era)] pub type RelayChainCurrentEra = StorageValue<_, EraIndex, ValueQuery>; - // /// The latest processed era on by, it should be always <= RelayChainCurrentEra + // /// The latest processed era of Homa, it should be always <= RelayChainCurrentEra // /// // /// ProcessedEra : EraIndex // #[pallet::storage] @@ -261,7 +261,8 @@ pub mod module { /// The total amount of void liquid currency. It's will not be issued, /// used to avoid newly issued LDOT to obtain the incoming staking income from relaychain. /// And it is guaranteed that the current exchange rate between liquid currency and staking - /// currency will not change. It will be reset to 0 at the end of the rebalance when new era. + /// currency will not change. It will be reset to 0 at the beginning of the rebalance when new + /// era. /// /// TotalVoidLiquid value: LiquidCurrencyAmount #[pallet::storage] @@ -289,8 +290,9 @@ pub mod module { pub type Unbondings = StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, EraIndex, Balance, ValueQuery>; - /// The weight for excution XCM msg on relaychain. Must be higher than - /// T::BaseXcmWeight + T::Weigher::weight(xcm_message_sended_by_homa)` on relaychain. + /// The weight limit for excution XCM msg on relaychain. Must be greater than the weight of + /// the XCM msg that sended by Homa, otherwise the execution of XCM msg will fail. + /// Consider all possible xcm msgs sended by Homa, and use the maximum as the limit. /// /// xcm_dest_weight: value: Weight #[pallet::storage] @@ -304,6 +306,7 @@ pub mod module { impl Hooks for Pallet { fn integrity_test() { assert!(!T::DefaultExchangeRate::get().is_zero()); + assert!(T::MintThreshold::get() >= T::XcmTransferFee::get()); } } @@ -321,14 +324,13 @@ pub mod module { // Ensure the amount is above the mint threshold. ensure!(amount > T::MintThreshold::get(), Error::::BelowMintThreshold); - // TODO: is that neccesary? + // Ensure the total staking currency will not exceed soft cap. ensure!( Self::get_total_staking_currency().saturating_add(amount) <= Self::get_staking_currency_soft_cap(), Error::::ExceededStakingCurrencySoftCap ); T::Currency::transfer(T::StakingCurrencyId::get(), &minter, &Self::account_id(), amount)?; - ToBondPool::::mutate(|pool| *pool = pool.saturating_add(amount)); // calculate the liquid amount by the current exchange rate. let liquid_amount = Self::convert_staking_to_liquid(amount)?; @@ -340,6 +342,7 @@ pub mod module { let liquid_add_to_void = liquid_amount.saturating_sub(liquid_issue_to_minter); T::Currency::deposit(T::LiquidCurrencyId::get(), &minter, liquid_issue_to_minter)?; + ToBondPool::::mutate(|pool| *pool = pool.saturating_add(amount)); TotalVoidLiquid::::mutate(|total| *total = total.saturating_add(liquid_add_to_void)); Self::deposit_event(Event::::Minted(minter, amount, liquid_issue_to_minter)); @@ -484,10 +487,10 @@ pub mod module { ensure!(new_era > *current_era, Error::::InvalidEraIndex); *current_era = new_era; - // reset void liquid to zero + // reset void liquid to zero firstly, to guarantee TotalVoidLiquid::::put(0); - // TODO: consider execute rebalance on on_idle, tut before the processing is completed, + // TODO: consider execute rebalance on on_idle, before the processing is completed, // the mint and request_redeem functions should be unavailable. // Rebalance: Self::process_scheduled_unbond(new_era)?; @@ -590,11 +593,9 @@ pub mod module { /// Calculate the total amount of staking currency belong to Homa. pub fn get_total_staking_currency() -> Balance { - let total_bonded: Balance = StakingLedgers::::iter().fold(Zero::zero(), |total_bonded, (_, ledger)| { + StakingLedgers::::iter().fold(Self::to_bond_pool(), |total_bonded, (_, ledger)| { total_bonded.saturating_add(ledger.bonded) - }); - let to_bond_pool = Self::to_bond_pool(); - total_bonded.saturating_add(to_bond_pool) + }) } /// Calculate the current exchange rate between the staking currency and liquid currency. @@ -851,8 +852,8 @@ pub mod module { Ok(()) } - /// Cross-chain transfer staking currency to subaccount and bond it on relaychain by XCM - /// message. + /// Cross-chain transfer staking currency to subaccount and send XCM message to the + /// relaychain to bond it. pub fn transfer_and_bond_to_relaychain(sub_account_index: SubAccountIndex, amount: Balance) -> DispatchResult { T::XcmTransfer::transfer( Self::account_id(), @@ -862,6 +863,7 @@ pub mod module { Self::xcm_dest_weight(), )?; + // subaccount will pay the XcmTransferFee, so the actual staking amount received should deduct it. let bond_amount = amount.saturating_sub(T::XcmTransferFee::get()); let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( T::RelayChainCallBuilder::utility_as_derivative_call( diff --git a/modules/homa/src/mock.rs b/modules/homa/src/mock.rs new file mode 100644 index 0000000000..59e7a262dc --- /dev/null +++ b/modules/homa/src/mock.rs @@ -0,0 +1,380 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2021 Acala Foundation. +// 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 . + +//! Mocks for the Homa module. + +#![cfg(test)] + +use super::*; +use cumulus_primitives_core::ParaId; +use frame_support::{ + ord_parameter_types, parameter_types, + traits::{Everything, Nothing}, +}; +use frame_system::{EnsureRoot, EnsureSignedBy, RawOrigin}; +use module_relaychain::RelayChainCallBuilder; +use module_support::mocks::MockAddressMapping; +use orml_traits::{parameter_type_with_key, XcmTransfer}; +use primitives::{Amount, TokenSymbol}; +use sp_core::H256; +use sp_io::hashing::blake2_256; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; +use xcm::latest::prelude::*; +use xcm_executor::traits::{InvertLocation, WeightBounds}; + +pub type AccountId = AccountId32; +pub type BlockNumber = u64; + +mod homa { + pub use super::super::*; +} + +pub const ALICE: AccountId = AccountId32::new([1u8; 32]); +pub const BOB: AccountId = AccountId32::new([2u8; 32]); +pub const CHARLIE: AccountId = AccountId32::new([3u8; 32]); +pub const DAVE: AccountId = AccountId32::new([255u8; 32]); +pub const INVALID_CALLER: AccountId = AccountId32::new([254u8; 32]); +pub const ACA: CurrencyId = CurrencyId::Token(TokenSymbol::ACA); +pub const DOT: CurrencyId = CurrencyId::Token(TokenSymbol::DOT); +pub const LDOT: CurrencyId = CurrencyId::Token(TokenSymbol::LDOT); +pub const MOCK_XCM_ACCOUNTID: AccountId = AccountId32::new([255u8; 32]); +pub const PARACHAIN_ID: u32 = 2000; + +/// For testing only. Does not check for overflow. +pub fn dollar(b: Balance) -> Balance { + b * 1_000_000_000_000 +} + +/// For testing only. Does not check for overflow. +pub fn cent(b: Balance) -> Balance { + b * 10_000_000_000 +} + +/// mock XCM transfer. +pub struct MockXcmTransfer; +impl XcmTransfer for MockXcmTransfer { + fn transfer( + who: AccountId, + currency_id: CurrencyId, + amount: Balance, + dest: MultiLocation, + _dest_weight: Weight, + ) -> DispatchResult { + match who { + INVALID_CALLER => Err(DispatchError::Other("invalid caller")), + _ => Ok(()), + }?; + match currency_id { + ACA => Err(DispatchError::Other("unacceptable currency id")), + _ => Ok(()), + }?; + + Currencies::withdraw(ACA, &who, amount) + } + + fn transfer_multi_asset( + _who: AccountId, + _asset: MultiAsset, + _dest: MultiLocation, + _dest_weight: Weight, + ) -> DispatchResult { + unimplemented!() + } +} + +/// mock XCM. +pub struct MockXcm; +impl InvertLocation for MockXcm { + fn invert_location(l: &MultiLocation) -> Result { + Ok(l.clone()) + } +} + +impl SendXcm for MockXcm { + fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { + let dest = dest.into(); + match dest { + MultiLocation { + parents: 1, + interior: Junctions::Here, + } => Ok(()), + _ => Err(SendError::CannotReachDestination(dest, msg)), + } + } +} + +impl ExecuteXcm for MockXcm { + fn execute_xcm_in_credit( + _origin: impl Into, + mut _message: Xcm, + _weight_limit: Weight, + _weight_credit: Weight, + ) -> Outcome { + Outcome::Complete(0) + } +} + +pub struct MockEnsureXcmOrigin; +impl EnsureOrigin for MockEnsureXcmOrigin { + type Success = MultiLocation; + fn try_origin(_o: Origin) -> Result { + Ok(MultiLocation::here()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> Origin { + Origin::from(RawOrigin::Signed(Default::default())) + } +} +pub struct MockWeigher; +impl WeightBounds for MockWeigher { + fn weight(_message: &mut Xcm) -> Result { + Ok(0) + } + + fn instr_weight(_message: &Instruction) -> Result { + Ok(0) + } +} + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = MockEnsureXcmOrigin; + type XcmRouter = MockXcm; + type ExecuteXcmOrigin = MockEnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = MockXcm; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = MockWeigher; + type LocationInverter = MockXcm; + type Origin = Origin; + type Call = Call; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = BlockNumber; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { + Default::default() + }; +} + +impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = (); + type MaxLocks = (); + type DustRemovalWhitelist = Nothing; +} + +parameter_types! { + pub const NativeTokenExistentialDeposit: Balance = 0; +} + +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = NativeTokenExistentialDeposit; + type AccountStore = frame_system::Pallet; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); +} + +pub type AdaptedBasicCurrency = module_currencies::BasicCurrencyAdapter; + +parameter_types! { + pub const GetNativeCurrencyId: CurrencyId = ACA; +} + +impl module_currencies::Config for Runtime { + type Event = Event; + type MultiCurrency = Tokens; + type NativeCurrency = AdaptedBasicCurrency; + type GetNativeCurrencyId = GetNativeCurrencyId; + type WeightInfo = (); + type AddressMapping = MockAddressMapping; + type EVMBridge = (); + type SweepOrigin = EnsureRoot; + type OnDust = (); +} + +pub struct MockConvertor; +impl Convert for MockConvertor { + fn convert(index: SubAccountIndex) -> MultiLocation { + let entropy = (b"modlpy/utilisuba", ParachainAccount::get(), index).using_encoded(blake2_256); + let subaccount = AccountId32::decode(&mut &entropy[..]).unwrap_or_default(); + MultiLocation::new( + 1, + X1(Junction::AccountId32 { + network: NetworkId::Any, + id: subaccount.into(), + }), + ) + } +} + +ord_parameter_types! { + pub const HomaAdmin: AccountId = DAVE; +} + +parameter_types! { + pub const LiquidCurrencyId: CurrencyId = LDOT; + pub const HomaPalletId: PalletId = PalletId(*b"aca/homa"); + pub MintThreshold: Balance = dollar(1); + pub RedeemThreshold: Balance = dollar(10); + pub DefaultExchangeRate: ExchangeRate = ExchangeRate::saturating_from_rational(1, 10); + pub ParachainAccount: AccountId = AccountId32::new([250u8; 32]); + pub ActiveSubAccountsIndexList: Vec = vec![0, 1, 2]; + pub SoftBondedCapPerSubAccount: Balance = dollar(10); + pub FastMatchKeepers: Vec = vec![CHARLIE, DAVE]; + pub const BondingDuration: EraIndex = 28; + pub const RelayChainUnbondingSlashingSpans: EraIndex = 7; + pub EstimatedRewardRatePerEra: Rate = Rate::saturating_from_rational(1, 100); + pub XcmTransferFee: Balance = cent(50); + pub XcmMessageFee: Balance = cent(50); + pub ParachainId: ParaId = ParaId::from(PARACHAIN_ID); +} + +impl Config for Runtime { + type Event = Event; + type Currency = Currencies; + type GovernanceOrigin = EnsureSignedBy; + type StakingCurrencyId = GetNativeCurrencyId; + type LiquidCurrencyId = LiquidCurrencyId; + type PalletId = HomaPalletId; + type DefaultExchangeRate = DefaultExchangeRate; + type MintThreshold = MintThreshold; + type RedeemThreshold = RedeemThreshold; + type ParachainAccount = ParachainAccount; + type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; + type SoftBondedCapPerSubAccount = SoftBondedCapPerSubAccount; + type FastMatchKeepers = FastMatchKeepers; + type BondingDuration = BondingDuration; + type RelayChainUnbondingSlashingSpans = RelayChainUnbondingSlashingSpans; + type EstimatedRewardRatePerEra = EstimatedRewardRatePerEra; + type XcmTransferFee = XcmTransferFee; + type XcmMessageFee = XcmMessageFee; + type RelayChainCallBuilder = RelayChainCallBuilder; + type XcmTransfer = MockXcmTransfer; + type SovereignSubAccountLocationConvert = MockConvertor; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Homa: homa::{Pallet, Call, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Tokens: orml_tokens::{Pallet, Storage, Event, Config}, + Currencies: module_currencies::{Pallet, Call, Event}, + PalletXcm: pallet_xcm::{Pallet, Call, Event, Origin}, + } +); + +pub struct ExtBuilder { + balances: Vec<(AccountId, CurrencyId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { balances: vec![] } + } +} + +impl ExtBuilder { + pub fn balances(mut self, balances: Vec<(AccountId, CurrencyId, Balance)>) -> Self { + self.balances = balances; + self + } + + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: self + .balances + .clone() + .into_iter() + .filter(|(_, currency_id, _)| *currency_id == DOT) + .map(|(account_id, _, initial_balance)| (account_id, initial_balance)) + .collect::>(), + } + .assimilate_storage(&mut t) + .unwrap(); + + orml_tokens::GenesisConfig:: { + balances: self + .balances + .into_iter() + .filter(|(_, currency_id, _)| *currency_id != DOT) + .collect::>(), + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} diff --git a/modules/homa/src/tests.rs b/modules/homa/src/tests.rs new file mode 100644 index 0000000000..8447de0b69 --- /dev/null +++ b/modules/homa/src/tests.rs @@ -0,0 +1,19 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2021 Acala Foundation. +// 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 . + +//! Unit tests for the Homa Module From cd2fe3cbdcb2d181e79a9939dc579741aa727fb6 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Tue, 30 Nov 2021 19:05:59 +0800 Subject: [PATCH 03/27] update --- Cargo.lock | 33 +++- modules/homa-xcm/Cargo.toml | 56 +++++++ modules/homa-xcm/src/lib.rs | 233 +++++++++++++++++++++++++++ modules/homa/Cargo.toml | 10 -- modules/homa/src/lib.rs | 303 +++++++++++++----------------------- modules/homa/src/mock.rs | 179 ++++----------------- modules/support/src/lib.rs | 14 ++ 7 files changed, 465 insertions(+), 363 deletions(-) create mode 100644 modules/homa-xcm/Cargo.toml create mode 100644 modules/homa-xcm/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index edbb6629be..1501c370f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5505,27 +5505,21 @@ name = "module-homa" version = "2.0.3" dependencies = [ "acala-primitives", - "cumulus-primitives-core", "frame-benchmarking", "frame-support", "frame-system", "module-currencies", - "module-relaychain", "module-support", "orml-tokens", "orml-traits", "pallet-balances", "pallet-staking", - "pallet-xcm", "parity-scale-codec", "scale-info", - "sp-arithmetic", "sp-core", "sp-io", "sp-runtime", "sp-std", - "xcm", - "xcm-executor", ] [[package]] @@ -5577,6 +5571,33 @@ dependencies = [ "sp-std", ] +[[package]] +name = "module-homa-xcm" +version = "2.0.3" +dependencies = [ + "acala-primitives", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "module-currencies", + "module-relaychain", + "module-support", + "orml-tokens", + "orml-traits", + "pallet-balances", + "pallet-staking", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-executor", +] + [[package]] name = "module-honzon" version = "2.0.3" diff --git a/modules/homa-xcm/Cargo.toml b/modules/homa-xcm/Cargo.toml new file mode 100644 index 0000000000..dc19e0f4d4 --- /dev/null +++ b/modules/homa-xcm/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "module-homa-xcm" +version = "2.0.3" +authors = ["Acala Developers"] +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } +scale-info = { version = "1.0", default-features = false, features = ["derive"] } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false, optional = true} +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } +primitives = { package = "acala-primitives", path = "../../primitives", default-features = false } +orml-traits = { path = "../../orml/traits", default-features = false } +module-support = { path = "../../modules/support", default-features = false } + +[dev-dependencies] +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } +module-currencies = { path = "../../modules/currencies" } +orml-tokens = { path = "../../orml/tokens" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12" } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12" } +module-relaychain = { path = "../relaychain", features = ["kusama"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-runtime/std", + "sp-core/std", + "sp-std/std", + "pallet-staking/std", + "pallet-xcm/std", + "xcm/std", + "primitives/std", + "orml-traits/std", + "module-support/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/modules/homa-xcm/src/lib.rs b/modules/homa-xcm/src/lib.rs new file mode 100644 index 0000000000..b301351797 --- /dev/null +++ b/modules/homa-xcm/src/lib.rs @@ -0,0 +1,233 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2021 Acala Foundation. +// 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 . + +//! Homa xcm module. + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::unused_unit)] + +use frame_support::{log, pallet_prelude::*, transactional, weights::Weight}; +use frame_system::pallet_prelude::*; +use module_support::{CallBuilder, HomaSubAccountXcm}; +use orml_traits::XcmTransfer; +use pallet_staking::EraIndex; +use primitives::{Balance, CurrencyId}; +use scale_info::TypeInfo; +use sp_runtime::traits::Convert; +use sp_std::{convert::From, prelude::*, vec, vec::Vec}; +use xcm::latest::prelude::*; + +pub use module::*; + +#[frame_support::pallet] +pub mod module { + use super::*; + + #[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, TypeInfo)] + pub enum HomaXcmOperation { + XtokensTransfer, + XcmWithdrawUnbonded, + XcmBondExtra, + XcmUnbond, + } + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_xcm::Config { + type Event: From> + IsType<::Event>; + + /// Origin represented Governance + type UpdateOrigin: EnsureOrigin<::Origin>; + + /// The currency id of the Staking asset + #[pallet::constant] + type StakingCurrencyId: Get; + + /// The account of parachain on the relaychain. + #[pallet::constant] + type ParachainAccount: Get; + + /// Unbonding slashing spans for unbonding on the relaychain. + #[pallet::constant] + type RelayChainUnbondingSlashingSpans: Get; + + /// The convert for convert sovereign subacocunt index to the MultiLocation where the + /// staking currencies are sent to. + type SovereignSubAccountLocationConvert: Convert; + + /// The Call builder for communicating with RelayChain via XCM messaging. + type RelayChainCallBuilder: CallBuilder; + + /// The interface to Cross-chain transfer. + type XcmTransfer: XcmTransfer; + } + + #[pallet::error] + pub enum Error { + /// The xcm operation have failed + XcmFailed, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// Xcm dest weight has been updated. \[xcm_operation, new_xcm_dest_weight\] + XcmDestWeightUpdated(HomaXcmOperation, Weight), + /// Xcm dest weight has been updated. \[xcm_operation, new_xcm_dest_weight\] + XcmFeeUpdated(HomaXcmOperation, Balance), + } + + /// The dest weight limit and fee for execution XCM msg sended by HomaXcm. Must be sufficient, + /// otherwise the execution of XCM msg on relaychain will fail. + /// + /// XcmDestWeightAndFee: map: HomaXcmOperation => (Weight, Balance) + #[pallet::storage] + #[pallet::getter(fn xcm_dest_weight_and_fee)] + pub type XcmDestWeightAndFee = + StorageMap<_, Twox64Concat, HomaXcmOperation, (Weight, Balance), ValueQuery>; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::hooks] + impl Hooks for Pallet {} + + #[pallet::call] + impl Pallet { + /// Sets the xcm_dest_weight and fee for XCM operation of HomaXcm. + /// + /// Parameters: + /// - `updates`: tumple of (HomaXcmOperation, WeightChange, FeeChange). + #[pallet::weight(10_000)] + #[transactional] + pub fn update_xcm_dest_weight_and_fee( + origin: OriginFor, + updates: Vec<(HomaXcmOperation, Option, Option)>, + ) -> DispatchResult { + T::UpdateOrigin::ensure_origin(origin)?; + + for (operation, weight_change, fee_change) in updates { + XcmDestWeightAndFee::::mutate(operation, |(weight, fee)| { + if let Some(new_weight) = weight_change { + *weight = new_weight; + Self::deposit_event(Event::::XcmDestWeightUpdated(operation, new_weight)); + } + if let Some(new_fee) = fee_change { + *fee = new_fee; + Self::deposit_event(Event::::XcmFeeUpdated(operation, new_fee)); + } + }); + } + + Ok(()) + } + } + + impl Pallet {} + + impl HomaSubAccountXcm for Pallet { + /// Cross-chain transfer staking currency to sub account on relaychain. + fn transfer_staking_to_sub_account( + sender: &T::AccountId, + sub_account_index: u16, + amount: Balance, + ) -> DispatchResult { + T::XcmTransfer::transfer( + sender.clone(), + T::StakingCurrencyId::get(), + amount, + T::SovereignSubAccountLocationConvert::convert(sub_account_index), + Self::xcm_dest_weight_and_fee(HomaXcmOperation::XtokensTransfer).0, + ) + } + + /// Send XCM message to the relaychain for sub account to withdraw_unbonded staking currency + /// and send it back. + fn withdraw_unbonded_from_sub_account(sub_account_index: u16, amount: Balance) -> DispatchResult { + let (xcm_dest_weight, xcm_fee) = Self::xcm_dest_weight_and_fee(HomaXcmOperation::XcmWithdrawUnbonded); + let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( + T::RelayChainCallBuilder::utility_as_derivative_call( + T::RelayChainCallBuilder::utility_batch_call(vec![ + T::RelayChainCallBuilder::staking_withdraw_unbonded(T::RelayChainUnbondingSlashingSpans::get()), + T::RelayChainCallBuilder::balances_transfer_keep_alive(T::ParachainAccount::get(), amount), + ]), + sub_account_index, + ), + xcm_fee, + xcm_dest_weight, + ); + let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); + log::debug!( + target: "homa-xcm", + "subaccount {:?} send XCM to withdraw unbonded {:?}, result: {:?}", + sub_account_index, amount, result + ); + + ensure!(result.is_ok(), Error::::XcmFailed); + Ok(()) + } + + /// Send XCM message to the relaychain for sub account to bond extra. + fn bond_extra_on_sub_account(sub_account_index: u16, amount: Balance) -> DispatchResult { + let (xcm_dest_weight, xcm_fee) = Self::xcm_dest_weight_and_fee(HomaXcmOperation::XcmBondExtra); + let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( + T::RelayChainCallBuilder::utility_as_derivative_call( + T::RelayChainCallBuilder::staking_bond_extra(amount), + sub_account_index, + ), + xcm_fee, + xcm_dest_weight, + ); + let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); + log::debug!( + target: "homa-xcm", + "subaccount {:?} send XCM to bond {:?}, result: {:?}", + sub_account_index, amount, result, + ); + + ensure!(result.is_ok(), Error::::XcmFailed); + Ok(()) + } + + /// Send XCM message to the relaychain for sub account to unbond. + fn unbond_on_sub_account(sub_account_index: u16, amount: Balance) -> DispatchResult { + let (xcm_dest_weight, xcm_fee) = Self::xcm_dest_weight_and_fee(HomaXcmOperation::XcmUnbond); + let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( + T::RelayChainCallBuilder::utility_as_derivative_call( + T::RelayChainCallBuilder::staking_unbond(amount), + sub_account_index, + ), + xcm_fee, + xcm_dest_weight, + ); + let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); + log::debug!( + target: "homa-xcm", + "subaccount {:?} send XCM to unbond {:?}, result: {:?}", + sub_account_index, amount, result + ); + + ensure!(result.is_ok(), Error::::XcmFailed); + Ok(()) + } + + /// The fee of cross-chain transfer is deducted from the recipient. + fn get_xcm_transfer_fee() -> Balance { + Self::xcm_dest_weight_and_fee(HomaXcmOperation::XtokensTransfer).1 + } + } +} diff --git a/modules/homa/Cargo.toml b/modules/homa/Cargo.toml index d037a56ee4..9fac1c4bac 100644 --- a/modules/homa/Cargo.toml +++ b/modules/homa/Cargo.toml @@ -10,13 +10,10 @@ scale-info = { version = "1.0", default-features = false, features = ["derive"] frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false, optional = true} frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } -sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } -pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } -xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12", default-features = false } primitives = { package = "acala-primitives", path = "../../primitives", default-features = false } orml-traits = { path = "../../orml/traits", default-features = false } module-support = { path = "../../modules/support", default-features = false } @@ -26,9 +23,6 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0 pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } module-currencies = { path = "../../modules/currencies" } orml-tokens = { path = "../../orml/tokens" } -xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12" } -cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12" } -module-relaychain = { path = "../relaychain", features = ["kusama"] } [features] default = ["std"] @@ -38,13 +32,10 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", - "sp-arithmetic/std", "sp-runtime/std", "sp-core/std", "sp-std/std", "pallet-staking/std", - "pallet-xcm/std", - "xcm/std", "primitives/std", "orml-traits/std", "module-support/std", @@ -53,6 +44,5 @@ runtime-benchmarks = [ "frame-benchmarking", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", ] try-runtime = ["frame-support/try-runtime"] diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 01c2bb25df..e3baa7949e 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -21,27 +21,18 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::unused_unit)] -use frame_support::{log, pallet_prelude::*, transactional, weights::Weight, BoundedVec, PalletId}; +use frame_support::{log, pallet_prelude::*, transactional, PalletId}; use frame_system::{ensure_signed, pallet_prelude::*}; -use module_support::{CallBuilder, ExchangeRate, ExchangeRateProvider, Rate}; -use orml_traits::{arithmetic::Signed, BalanceStatus, MultiCurrency, MultiCurrencyExtended, XcmTransfer}; +use module_support::{ExchangeRate, ExchangeRateProvider, HomaSubAccountXcm, Rate}; +use orml_traits::MultiCurrency; use pallet_staking::EraIndex; use primitives::{Balance, CurrencyId}; use scale_info::TypeInfo; -use sp_arithmetic::traits::CheckedRem; use sp_runtime::{ - traits::{AccountIdConversion, BlockNumberProvider, Bounded, Convert, One, Saturating, Zero}, - ArithmeticError, FixedPointNumber, Permill, + traits::{AccountIdConversion, Bounded, One, Saturating, Zero}, + ArithmeticError, FixedPointNumber, }; -use sp_std::{ - cmp::{min, Ordering}, - convert::{From, TryFrom, TryInto}, - ops::Mul, - prelude::*, - vec, - vec::Vec, -}; -use xcm::latest::prelude::*; +use sp_std::{cmp::Ordering, convert::From, prelude::*, vec, vec::Vec}; pub use module::*; @@ -101,14 +92,12 @@ pub mod module { } } - pub type SubAccountIndex = u16; - #[pallet::config] - pub trait Config: frame_system::Config + pallet_xcm::Config { + pub trait Config: frame_system::Config { type Event: From> + IsType<::Event>; /// Multi-currency support for asset management - type Currency: MultiCurrencyExtended; + type Currency: MultiCurrency; /// Origin represented Governance type GovernanceOrigin: EnsureOrigin<::Origin>; @@ -129,61 +118,21 @@ pub mod module { #[pallet::constant] type DefaultExchangeRate: Get; - /// The threshold for mint operation in staking currency. + /// Vault reward of Homa protocol #[pallet::constant] - type MintThreshold: Get; - - /// The threshold for redeem operation in liquid currency. - #[pallet::constant] - type RedeemThreshold: Get; - - /// The account of parachain on the relaychain. - #[pallet::constant] - type ParachainAccount: Get; + type TreasuryAccount: Get; /// The index list of active Homa subaccounts. /// `active` means these subaccounts can continue do bond/unbond operations by Homa. #[pallet::constant] - type ActiveSubAccountsIndexList: Get>; - - /// The bonded soft cap for each subaccount, use len(ActiveSubAccountsIndexList) * - /// SoftBondedCapPerSubAccount as the staking currency cap. - #[pallet::constant] - type SoftBondedCapPerSubAccount: Get; - - /// The keepers list which are allowed to do fast match redeem request. - #[pallet::constant] - type FastMatchKeepers: Get>; + type ActiveSubAccountsIndexList: Get>; /// Number of eras for unbonding is expired on relaychain. #[pallet::constant] type BondingDuration: Get; - /// Unbonding slashing spans for unbonding on the relaychain. - #[pallet::constant] - type RelayChainUnbondingSlashingSpans: Get; - - /// The estimated staking reward rate per era on relaychain. - #[pallet::constant] - type EstimatedRewardRatePerEra: Get; - - /// The fixed staking currency cost of transaction fee for XCMTransfer. - #[pallet::constant] - type XcmTransferFee: Get; - - /// The fixed staking currency cost of extra fee for xcm message - #[pallet::constant] - type XcmMessageFee: Get; - - /// The Call builder for communicating with RelayChain via XCM messaging. - type RelayChainCallBuilder: CallBuilder; - - /// The interface to Cross-chain transfer. - type XcmTransfer: XcmTransfer; - - /// The convert for convert sovereign subacocunt index to the MultiLocation where the - /// staking currencies are sent to. - type SovereignSubAccountLocationConvert: Convert; + /// The HomaXcm to manage the staking of sub-account on relaychain. + type HomaXcm: HomaSubAccountXcm; } #[pallet::error] @@ -192,16 +141,12 @@ pub mod module { BelowMintThreshold, /// The redeem amount to request is below the threshold. BelowRedeemThreshold, - /// The caller is not in `FastMatchKeepers` list. - NotAllowedKeeper, /// The mint will cause staking currency of Homa exceed the soft cap. ExceededStakingCurrencySoftCap, /// UnclaimedRedemption is not enough, this error is not expected. InsufficientUnclaimedRedemption, /// Invalid era index to bump, must be greater than RelayChainCurrentEra InvalidEraIndex, - /// The xcm operation have failed - XcmFailed, } #[pallet::event] @@ -219,16 +164,23 @@ pub mod module { RedeemedByFastMatch(T::AccountId, Balance, Balance, Balance), /// The redeemer withdraw expired redemption. \[redeemer, redeption_amount\] WithdrawRedemption(T::AccountId, Balance), - /// The redeemer withdraw expired redemption. \[redeemer, redeption_amount\] - XcmDestWeightUpdated(Weight), /// The current era has been bumped. \[new_era_index\] CurrentEraBumped(EraIndex), /// The bonded amount of subaccount's ledger has been updated. \[sub_account_index, /// new_bonded_amount\] - LedgerBondedUpdated(SubAccountIndex, Balance), + LedgerBondedUpdated(u16, Balance), /// The unlocking of subaccount's ledger has been updated. \[sub_account_index, /// new_unlocking\] - LedgerUnlockingUpdated(SubAccountIndex, Vec), + LedgerUnlockingUpdated(u16, Vec), + /// The soft bonded cap of per sub account has been updated. \[cap_amount\] + SoftBondedCapPerSubAccountUpdated(Balance), + /// The estimated reward rate per era of relaychain staking has been updated. + /// \[reward_rate\] + EstimatedRewardRatePerEraUpdated(Rate), + /// The threshold to mint has been updated. \[mint_threshold\] + MintThresholdUpdated(Balance), + /// The threshold to redeem has been updated. \[redeem_threshold\] + RedeemThresholdUpdated(Balance), } /// The current era of relaychain @@ -247,10 +199,10 @@ pub mod module { /// The staking ledger of Homa subaccounts. /// - /// StakingLedgers map: SubAccountIndex => Option + /// StakingLedgers map: u16 => Option #[pallet::storage] #[pallet::getter(fn staking_ledgers)] - pub type StakingLedgers = StorageMap<_, Twox64Concat, SubAccountIndex, StakingLedger, OptionQuery>; + pub type StakingLedgers = StorageMap<_, Twox64Concat, u16, StakingLedger, OptionQuery>; /// The total staking currency to bond on relaychain when new era, /// and that is available to be match fast redeem request. @@ -291,25 +243,40 @@ pub mod module { pub type Unbondings = StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, EraIndex, Balance, ValueQuery>; - /// The weight limit for excution XCM msg on relaychain. Must be greater than the weight of - /// the XCM msg that sended by Homa, otherwise the execution of XCM msg will fail. - /// Consider all possible xcm msgs sended by Homa, and use the maximum as the limit. + /// The estimated staking reward rate per era on relaychain. /// - /// xcm_dest_weight: value: Weight + /// EstimatedRewardRatePerEra: value: Rate #[pallet::storage] - #[pallet::getter(fn xcm_dest_weight)] - pub type XcmDestWeight = StorageValue<_, Weight, ValueQuery>; + #[pallet::getter(fn estimated_reward_rate_per_era)] + pub type EstimatedRewardRatePerEra = StorageValue<_, Rate, ValueQuery>; + + /// Th maximum amount of bonded staking currency for a single sub on relaychain to obtain the + /// best staking rewards. + /// + /// SoftBondedCapPerSubAccount: value: Balance + #[pallet::storage] + #[pallet::getter(fn soft_bonded_cap_per_sub_account)] + pub type SoftBondedCapPerSubAccount = StorageValue<_, Balance, ValueQuery>; + + /// Th staking amount of threshold to mint. + /// + /// MintThreshold: value: Balance + #[pallet::storage] + #[pallet::getter(fn mint_threshold)] + pub type MintThreshold = StorageValue<_, Balance, ValueQuery>; + + /// Th liquid amount of threshold to redeem. + /// + /// RedeemThreshold: value: Balance + #[pallet::storage] + #[pallet::getter(fn redeem_threshold)] + pub type RedeemThreshold = StorageValue<_, Balance, ValueQuery>; #[pallet::pallet] pub struct Pallet(_); #[pallet::hooks] - impl Hooks for Pallet { - fn integrity_test() { - assert!(!T::DefaultExchangeRate::get().is_zero()); - assert!(T::MintThreshold::get() >= T::XcmTransferFee::get()); - } - } + impl Hooks for Pallet {} #[pallet::call] impl Pallet { @@ -323,7 +290,7 @@ pub mod module { let minter = ensure_signed(origin)?; // Ensure the amount is above the mint threshold. - ensure!(amount > T::MintThreshold::get(), Error::::BelowMintThreshold); + ensure!(amount >= Self::mint_threshold(), Error::::BelowMintThreshold); // Ensure the total staking currency will not exceed soft cap. ensure!( @@ -336,7 +303,7 @@ pub mod module { // calculate the liquid amount by the current exchange rate. let liquid_amount = Self::convert_staking_to_liquid(amount)?; let liquid_issue_to_minter = Rate::one() - .saturating_add(T::EstimatedRewardRatePerEra::get()) + .saturating_add(Self::estimated_reward_rate_per_era()) .reciprocal() .expect("shouldn't be invalid!") .saturating_mul_int(liquid_amount); @@ -377,7 +344,7 @@ pub mod module { let liquid_currency_id = T::LiquidCurrencyId::get(); ensure!( - (!previous_request_amount.is_zero() && amount.is_zero()) || amount >= T::RedeemThreshold::get(), + (!previous_request_amount.is_zero() && amount.is_zero()) || amount >= Self::redeem_threshold(), Error::::BelowRedeemThreshold ); @@ -425,19 +392,18 @@ pub mod module { } /// Execute fast match for specific redeem requests. - /// Caller must be in `FastMatchKeepers` list. /// /// Parameters: /// - `redeemer_list`: The list of redeem requests to execute fast redeem. #[pallet::weight(10_000)] #[transactional] pub fn fast_match_redeems(origin: OriginFor, redeemer_list: Vec) -> DispatchResult { - let who = ensure_signed(origin)?; - ensure!(T::FastMatchKeepers::get().contains(&who), Error::::NotAllowedKeeper); + let _ = ensure_signed(origin)?; for redeemer in redeemer_list { Self::do_fast_match_redeem(&redeemer)?; } + Ok(()) } @@ -454,7 +420,7 @@ pub mod module { Unbondings::::iter_prefix(&redeemer) .filter(|(era_index, _)| era_index <= &Self::relay_chain_current_era()) .for_each(|(expired_era_index, unbonded)| { - available_staking = available_staking.saturating_add(available_staking); + available_staking = available_staking.saturating_add(unbonded); Unbondings::::remove(&redeemer, expired_era_index); }); UnclaimedRedemption::::try_mutate(|total| -> DispatchResult { @@ -462,7 +428,7 @@ pub mod module { .checked_sub(available_staking) .ok_or(Error::::InsufficientUnclaimedRedemption)?; Ok(()) - }); + })?; T::Currency::transfer( T::StakingCurrencyId::get(), &Self::account_id(), @@ -512,7 +478,7 @@ pub mod module { #[transactional] pub fn update_ledgers( origin: OriginFor, - updates: Vec<(SubAccountIndex, Option, Option>)>, + updates: Vec<(u16, Option, Option>)>, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; @@ -531,21 +497,42 @@ pub mod module { Ok(()) } - /// Sets the xcm_dest_weight for XCM staking operations. + /// Sets the params of Homa. /// Requires `GovernanceOrigin` /// /// Parameters: - /// - `xcm_dest_weight`: The new weight for XCM staking operations. + /// - `soft_bonded_cap_per_sub_account`: soft cap of staking amount for a single nominator + /// on relaychain to obtain the best staking rewards. + /// - `estimated_reward_rate_per_era`: the esstaking yield of each era on the current relay + /// chain #[pallet::weight(10_000)] #[transactional] - pub fn update_xcm_dest_weight( + pub fn update_homa_params( origin: OriginFor, - #[pallet::compact] xcm_dest_weight: Weight, + soft_bonded_cap_per_sub_account: Option, + estimated_reward_rate_per_era: Option, + mint_threshold: Option, + redeem_threshold: Option, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; - XcmDestWeight::::put(xcm_dest_weight); - Self::deposit_event(Event::::XcmDestWeightUpdated(xcm_dest_weight)); + if let Some(cap) = soft_bonded_cap_per_sub_account { + SoftBondedCapPerSubAccount::::put(cap); + Self::deposit_event(Event::::SoftBondedCapPerSubAccountUpdated(cap)); + } + if let Some(rate) = estimated_reward_rate_per_era { + EstimatedRewardRatePerEra::::put(rate); + Self::deposit_event(Event::::EstimatedRewardRatePerEraUpdated(rate)); + } + if let Some(threshold) = mint_threshold { + MintThreshold::::put(threshold); + Self::deposit_event(Event::::MintThresholdUpdated(threshold)); + } + if let Some(threshold) = redeem_threshold { + RedeemThreshold::::put(threshold); + Self::deposit_event(Event::::RedeemThresholdUpdated(threshold)); + } + Ok(()) } } @@ -557,7 +544,7 @@ pub mod module { } fn do_update_ledger( - sub_account_index: SubAccountIndex, + sub_account_index: u16, f: impl FnOnce(&mut StakingLedger) -> sp_std::result::Result, ) -> sp_std::result::Result { StakingLedgers::::try_mutate_exists(sub_account_index, |maybe_ledger| { @@ -589,7 +576,8 @@ pub mod module { /// Get the soft cap of total staking currency of Homa. /// Soft cap = ActiveSubAccountsIndexList.len() * SoftBondedCapPerSubAccount pub fn get_staking_currency_soft_cap() -> Balance { - T::SoftBondedCapPerSubAccount::get().saturating_mul(T::ActiveSubAccountsIndexList::get().len() as Balance) + Self::soft_bonded_cap_per_sub_account() + .saturating_mul(T::ActiveSubAccountsIndexList::get().len() as Balance) } /// Calculate the total amount of staking currency belong to Homa. @@ -652,7 +640,7 @@ pub mod module { request_amount } else { // if cannot fast match the request amount fully, at least keep RedeemThreshold as remainer. - liquid_limit_at_fee_rate.min(request_amount.saturating_sub(T::RedeemThreshold::get())) + liquid_limit_at_fee_rate.min(request_amount.saturating_sub(Self::redeem_threshold())) }; if !actual_liquid_to_redeem.is_zero() { @@ -712,7 +700,7 @@ pub mod module { let (new_ledger, expired_unlocking) = ledger.consolidate_unlocked(new_era); if !expired_unlocking.is_zero() { - Self::withdraw_unbonded_from_relaychain(sub_account_index, expired_unlocking)?; + T::HomaXcm::withdraw_unbonded_from_sub_account(sub_account_index, expired_unlocking)?; // udpate ledger Self::do_update_ledger(sub_account_index, |before| -> DispatchResult { @@ -744,26 +732,29 @@ pub mod module { new_era ); - let xcm_transfer_fee = T::XcmTransferFee::get(); - let bonded_list: Vec<(SubAccountIndex, Balance)> = T::ActiveSubAccountsIndexList::get() + let xcm_transfer_fee = T::HomaXcm::get_xcm_transfer_fee(); + let bonded_list: Vec<(u16, Balance)> = T::ActiveSubAccountsIndexList::get() .iter() .map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded)) .collect(); - let (distribution, remainer) = distribute_increment::( + let (distribution, remainer) = distribute_increment::( bonded_list, Self::to_bond_pool(), - Some(T::SoftBondedCapPerSubAccount::get()), + Some(Self::soft_bonded_cap_per_sub_account()), Some(xcm_transfer_fee), ); // subaccounts execute the distribution for (sub_account_index, amount) in distribution { if !amount.is_zero() { - Self::transfer_and_bond_to_relaychain(sub_account_index, amount)?; + T::HomaXcm::transfer_staking_to_sub_account(&Self::account_id(), sub_account_index, amount)?; + + let bond_amount = amount.saturating_sub(xcm_transfer_fee); + T::HomaXcm::bond_extra_on_sub_account(sub_account_index, bond_amount)?; // udpate ledger Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { - ledger.bonded = ledger.bonded.saturating_add(amount.saturating_sub(xcm_transfer_fee)); + ledger.bonded = ledger.bonded.saturating_add(bond_amount); Ok(()) })?; } @@ -795,17 +786,16 @@ pub mod module { // calculate the distribution for unbond let staking_amount_to_unbond = Self::convert_liquid_to_staking(total_redeem_amount)?; - let bonded_list: Vec<(SubAccountIndex, Balance)> = T::ActiveSubAccountsIndexList::get() + let bonded_list: Vec<(u16, Balance)> = T::ActiveSubAccountsIndexList::get() .iter() .map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded)) .collect(); - let (distribution, _) = - distribute_decrement::(bonded_list, staking_amount_to_unbond, None, None); + let (distribution, _) = distribute_decrement::(bonded_list, staking_amount_to_unbond, None, None); // subaccounts execute the distribution for (sub_account_index, unbond_amount) in distribution { if !unbond_amount.is_zero() { - Self::unbond_on_relaychain(sub_account_index, unbond_amount)?; + T::HomaXcm::unbond_on_sub_account(sub_account_index, unbond_amount)?; // udpate ledger Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { @@ -820,88 +810,7 @@ pub mod module { } // burn total_redeem_amount. - T::Currency::withdraw(T::LiquidCurrencyId::get(), &Self::account_id(), total_redeem_amount)?; - - Ok(()) - } - - /// Send XCM message to the relaychain to withdraw_unbonded staking currency from - /// subaccount. - pub fn withdraw_unbonded_from_relaychain( - sub_account_index: SubAccountIndex, - amount: Balance, - ) -> DispatchResult { - let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( - T::RelayChainCallBuilder::utility_as_derivative_call( - T::RelayChainCallBuilder::utility_batch_call(vec![ - T::RelayChainCallBuilder::staking_withdraw_unbonded(T::RelayChainUnbondingSlashingSpans::get()), - T::RelayChainCallBuilder::balances_transfer_keep_alive(T::ParachainAccount::get(), amount), - ]), - sub_account_index, - ), - T::XcmMessageFee::get(), - Self::xcm_dest_weight(), - ); - - let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); - log::debug!( - target: "homa", - "subaccount {:?} send XCM to withdraw unbonded {:?} on relaychain result: {:?}", - sub_account_index, amount, result - ); - ensure!(result.is_ok(), Error::::XcmFailed); - Ok(()) - } - - /// Cross-chain transfer staking currency to subaccount and send XCM message to the - /// relaychain to bond it. - pub fn transfer_and_bond_to_relaychain(sub_account_index: SubAccountIndex, amount: Balance) -> DispatchResult { - T::XcmTransfer::transfer( - Self::account_id(), - T::StakingCurrencyId::get(), - amount, - T::SovereignSubAccountLocationConvert::convert(sub_account_index), - Self::xcm_dest_weight(), - )?; - - // subaccount will pay the XcmTransferFee, so the actual staking amount received should deduct it. - let bond_amount = amount.saturating_sub(T::XcmTransferFee::get()); - let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( - T::RelayChainCallBuilder::utility_as_derivative_call( - T::RelayChainCallBuilder::staking_bond_extra(bond_amount), - sub_account_index, - ), - T::XcmMessageFee::get(), - Self::xcm_dest_weight(), - ); - let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); - log::debug!( - target: "homa", - "subaccount {:?} send XCM to bond {:?} on relaychain result: {:?}", - sub_account_index, bond_amount, result, - ); - ensure!(result.is_ok(), Error::::XcmFailed); - Ok(()) - } - - /// Send XCM message to the relaychain to unbond subaccount. - pub fn unbond_on_relaychain(sub_account_index: SubAccountIndex, amount: Balance) -> DispatchResult { - let xcm_message = T::RelayChainCallBuilder::finalize_call_into_xcm_message( - T::RelayChainCallBuilder::utility_as_derivative_call( - T::RelayChainCallBuilder::staking_unbond(amount), - sub_account_index, - ), - T::XcmMessageFee::get(), - Self::xcm_dest_weight(), - ); - let result = pallet_xcm::Pallet::::send_xcm(Here, Parent, xcm_message); - log::debug!( - target: "homa", - "subaccount {:?} send XCM to unbond {:?} on relaychain result: {:?}", - sub_account_index, amount, result - ); - ensure!(result.is_ok(), Error::::XcmFailed); - Ok(()) + T::Currency::withdraw(T::LiquidCurrencyId::get(), &Self::account_id(), total_redeem_amount) } } diff --git a/modules/homa/src/mock.rs b/modules/homa/src/mock.rs index 59e7a262dc..6c5cee597f 100644 --- a/modules/homa/src/mock.rs +++ b/modules/homa/src/mock.rs @@ -21,21 +21,16 @@ #![cfg(test)] use super::*; -use cumulus_primitives_core::ParaId; use frame_support::{ ord_parameter_types, parameter_types, traits::{Everything, Nothing}, }; -use frame_system::{EnsureRoot, EnsureSignedBy, RawOrigin}; -use module_relaychain::RelayChainCallBuilder; +use frame_system::{EnsureRoot, EnsureSignedBy}; use module_support::mocks::MockAddressMapping; -use orml_traits::{parameter_type_with_key, XcmTransfer}; +use orml_traits::parameter_type_with_key; use primitives::{Amount, TokenSymbol}; use sp_core::H256; -use sp_io::hashing::blake2_256; use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; -use xcm::latest::prelude::*; -use xcm_executor::traits::{InvertLocation, WeightBounds}; pub type AccountId = AccountId32; pub type BlockNumber = u64; @@ -47,13 +42,11 @@ mod homa { pub const ALICE: AccountId = AccountId32::new([1u8; 32]); pub const BOB: AccountId = AccountId32::new([2u8; 32]); pub const CHARLIE: AccountId = AccountId32::new([3u8; 32]); -pub const DAVE: AccountId = AccountId32::new([255u8; 32]); -pub const INVALID_CALLER: AccountId = AccountId32::new([254u8; 32]); -pub const ACA: CurrencyId = CurrencyId::Token(TokenSymbol::ACA); -pub const DOT: CurrencyId = CurrencyId::Token(TokenSymbol::DOT); -pub const LDOT: CurrencyId = CurrencyId::Token(TokenSymbol::LDOT); -pub const MOCK_XCM_ACCOUNTID: AccountId = AccountId32::new([255u8; 32]); -pub const PARACHAIN_ID: u32 = 2000; +pub const DAVE: AccountId = AccountId32::new([4u8; 32]); +pub const HOMA_TREASURY: AccountId = AccountId32::new([255u8; 32]); +pub const NATIVE_CURRENCY_ID: CurrencyId = CurrencyId::Token(TokenSymbol::ACA); +pub const STAKING_CURRENCY_ID: CurrencyId = CurrencyId::Token(TokenSymbol::DOT); +pub const LIQUID_CURRENCY_ID: CurrencyId = CurrencyId::Token(TokenSymbol::LDOT); /// For testing only. Does not check for overflow. pub fn dollar(b: Balance) -> Balance { @@ -66,109 +59,29 @@ pub fn cent(b: Balance) -> Balance { } /// mock XCM transfer. -pub struct MockXcmTransfer; -impl XcmTransfer for MockXcmTransfer { - fn transfer( - who: AccountId, - currency_id: CurrencyId, - amount: Balance, - dest: MultiLocation, - _dest_weight: Weight, - ) -> DispatchResult { - match who { - INVALID_CALLER => Err(DispatchError::Other("invalid caller")), - _ => Ok(()), - }?; - match currency_id { - ACA => Err(DispatchError::Other("unacceptable currency id")), - _ => Ok(()), - }?; - - Currencies::withdraw(ACA, &who, amount) +pub struct MockHomaSubAccountXcm; +impl HomaSubAccountXcm for MockHomaSubAccountXcm { + fn transfer_staking_to_sub_account(sender: &AccountId, _: u16, amount: Balance) -> DispatchResult { + Currencies::withdraw(StakingCurrencyId::get(), sender, amount) } - fn transfer_multi_asset( - _who: AccountId, - _asset: MultiAsset, - _dest: MultiLocation, - _dest_weight: Weight, - ) -> DispatchResult { - unimplemented!() + fn withdraw_unbonded_from_sub_account(_: u16, _: Balance) -> DispatchResult { + Ok(()) } -} -/// mock XCM. -pub struct MockXcm; -impl InvertLocation for MockXcm { - fn invert_location(l: &MultiLocation) -> Result { - Ok(l.clone()) + fn bond_extra_on_sub_account(_: u16, _: Balance) -> DispatchResult { + Ok(()) } -} -impl SendXcm for MockXcm { - fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { - let dest = dest.into(); - match dest { - MultiLocation { - parents: 1, - interior: Junctions::Here, - } => Ok(()), - _ => Err(SendError::CannotReachDestination(dest, msg)), - } + fn unbond_on_sub_account(_: u16, _: Balance) -> DispatchResult { + Ok(()) } -} -impl ExecuteXcm for MockXcm { - fn execute_xcm_in_credit( - _origin: impl Into, - mut _message: Xcm, - _weight_limit: Weight, - _weight_credit: Weight, - ) -> Outcome { - Outcome::Complete(0) + fn get_xcm_transfer_fee() -> Balance { + cent(10) } } -pub struct MockEnsureXcmOrigin; -impl EnsureOrigin for MockEnsureXcmOrigin { - type Success = MultiLocation; - fn try_origin(_o: Origin) -> Result { - Ok(MultiLocation::here()) - } - - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> Origin { - Origin::from(RawOrigin::Signed(Default::default())) - } -} -pub struct MockWeigher; -impl WeightBounds for MockWeigher { - fn weight(_message: &mut Xcm) -> Result { - Ok(0) - } - - fn instr_weight(_message: &Instruction) -> Result { - Ok(0) - } -} - -impl pallet_xcm::Config for Runtime { - type Event = Event; - type SendXcmOrigin = MockEnsureXcmOrigin; - type XcmRouter = MockXcm; - type ExecuteXcmOrigin = MockEnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - type XcmExecutor = MockXcm; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = MockWeigher; - type LocationInverter = MockXcm; - type Origin = Origin; - type Call = Call; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; -} - parameter_types! { pub const BlockHashCount: u64 = 250; } @@ -236,7 +149,7 @@ impl pallet_balances::Config for Runtime { pub type AdaptedBasicCurrency = module_currencies::BasicCurrencyAdapter; parameter_types! { - pub const GetNativeCurrencyId: CurrencyId = ACA; + pub const GetNativeCurrencyId: CurrencyId = NATIVE_CURRENCY_ID; } impl module_currencies::Config for Runtime { @@ -251,65 +164,32 @@ impl module_currencies::Config for Runtime { type OnDust = (); } -pub struct MockConvertor; -impl Convert for MockConvertor { - fn convert(index: SubAccountIndex) -> MultiLocation { - let entropy = (b"modlpy/utilisuba", ParachainAccount::get(), index).using_encoded(blake2_256); - let subaccount = AccountId32::decode(&mut &entropy[..]).unwrap_or_default(); - MultiLocation::new( - 1, - X1(Junction::AccountId32 { - network: NetworkId::Any, - id: subaccount.into(), - }), - ) - } -} - ord_parameter_types! { pub const HomaAdmin: AccountId = DAVE; } parameter_types! { - pub const LiquidCurrencyId: CurrencyId = LDOT; + pub const StakingCurrencyId: CurrencyId = STAKING_CURRENCY_ID; + pub const LiquidCurrencyId: CurrencyId = LIQUID_CURRENCY_ID; pub const HomaPalletId: PalletId = PalletId(*b"aca/homa"); - pub MintThreshold: Balance = dollar(1); - pub RedeemThreshold: Balance = dollar(10); + pub const TreasuryAccount: AccountId = HOMA_TREASURY; pub DefaultExchangeRate: ExchangeRate = ExchangeRate::saturating_from_rational(1, 10); - pub ParachainAccount: AccountId = AccountId32::new([250u8; 32]); - pub ActiveSubAccountsIndexList: Vec = vec![0, 1, 2]; - pub SoftBondedCapPerSubAccount: Balance = dollar(10); - pub FastMatchKeepers: Vec = vec![CHARLIE, DAVE]; + pub ActiveSubAccountsIndexList: Vec = vec![0, 1, 2]; pub const BondingDuration: EraIndex = 28; - pub const RelayChainUnbondingSlashingSpans: EraIndex = 7; - pub EstimatedRewardRatePerEra: Rate = Rate::saturating_from_rational(1, 100); - pub XcmTransferFee: Balance = cent(50); - pub XcmMessageFee: Balance = cent(50); - pub ParachainId: ParaId = ParaId::from(PARACHAIN_ID); } impl Config for Runtime { type Event = Event; type Currency = Currencies; type GovernanceOrigin = EnsureSignedBy; - type StakingCurrencyId = GetNativeCurrencyId; + type StakingCurrencyId = StakingCurrencyId; type LiquidCurrencyId = LiquidCurrencyId; type PalletId = HomaPalletId; + type TreasuryAccount = TreasuryAccount; type DefaultExchangeRate = DefaultExchangeRate; - type MintThreshold = MintThreshold; - type RedeemThreshold = RedeemThreshold; - type ParachainAccount = ParachainAccount; type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; - type SoftBondedCapPerSubAccount = SoftBondedCapPerSubAccount; - type FastMatchKeepers = FastMatchKeepers; type BondingDuration = BondingDuration; - type RelayChainUnbondingSlashingSpans = RelayChainUnbondingSlashingSpans; - type EstimatedRewardRatePerEra = EstimatedRewardRatePerEra; - type XcmTransferFee = XcmTransferFee; - type XcmMessageFee = XcmMessageFee; - type RelayChainCallBuilder = RelayChainCallBuilder; - type XcmTransfer = MockXcmTransfer; - type SovereignSubAccountLocationConvert = MockConvertor; + type HomaXcm = MockHomaSubAccountXcm; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -326,7 +206,6 @@ frame_support::construct_runtime!( Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Tokens: orml_tokens::{Pallet, Storage, Event, Config}, Currencies: module_currencies::{Pallet, Call, Event}, - PalletXcm: pallet_xcm::{Pallet, Call, Event, Origin}, } ); @@ -356,7 +235,7 @@ impl ExtBuilder { .balances .clone() .into_iter() - .filter(|(_, currency_id, _)| *currency_id == DOT) + .filter(|(_, currency_id, _)| *currency_id == NATIVE_CURRENCY_ID) .map(|(account_id, _, initial_balance)| (account_id, initial_balance)) .collect::>(), } @@ -367,7 +246,7 @@ impl ExtBuilder { balances: self .balances .into_iter() - .filter(|(_, currency_id, _)| *currency_id != DOT) + .filter(|(_, currency_id, _)| *currency_id != NATIVE_CURRENCY_ID) .collect::>(), } .assimilate_storage(&mut t) diff --git a/modules/support/src/lib.rs b/modules/support/src/lib.rs index 20a54101d3..36d14168bc 100644 --- a/modules/support/src/lib.rs +++ b/modules/support/src/lib.rs @@ -654,3 +654,17 @@ pub trait OnNewEra { pub trait NomineesProvider { fn nominees() -> Vec; } + +pub trait HomaSubAccountXcm { + /// Cross-chain transfer staking currency to sub account on relaychain. + fn transfer_staking_to_sub_account(sender: &AccountId, sub_account_index: u16, amount: Balance) -> DispatchResult; + /// Send XCM message to the relaychain for sub account to withdraw_unbonded staking currency and + /// send it back. + fn withdraw_unbonded_from_sub_account(sub_account_index: u16, amount: Balance) -> DispatchResult; + /// Send XCM message to the relaychain for sub account to bond extra. + fn bond_extra_on_sub_account(sub_account_index: u16, amount: Balance) -> DispatchResult; + /// Send XCM message to the relaychain for sub account to unbond. + fn unbond_on_sub_account(sub_account_index: u16, amount: Balance) -> DispatchResult; + /// The fee of cross-chain transfer is deducted from the recipient. + fn get_xcm_transfer_fee() -> Balance; +} From b0def4373fe8752f833e55637bab2114e3f047ef Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Tue, 30 Nov 2021 23:39:23 +0800 Subject: [PATCH 04/27] draw commission from staking reward --- modules/homa/src/lib.rs | 73 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index e3baa7949e..cd2b001ab6 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -23,13 +23,13 @@ use frame_support::{log, pallet_prelude::*, transactional, PalletId}; use frame_system::{ensure_signed, pallet_prelude::*}; -use module_support::{ExchangeRate, ExchangeRateProvider, HomaSubAccountXcm, Rate}; +use module_support::{ExchangeRate, ExchangeRateProvider, HomaSubAccountXcm, Rate, Ratio}; use orml_traits::MultiCurrency; use pallet_staking::EraIndex; use primitives::{Balance, CurrencyId}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AccountIdConversion, Bounded, One, Saturating, Zero}, + traits::{AccountIdConversion, Bounded, One, Saturating, UniqueSaturatedInto, Zero}, ArithmeticError, FixedPointNumber, }; use sp_std::{cmp::Ordering, convert::From, prelude::*, vec, vec::Vec}; @@ -181,6 +181,8 @@ pub mod module { MintThresholdUpdated(Balance), /// The threshold to redeem has been updated. \[redeem_threshold\] RedeemThresholdUpdated(Balance), + /// The commission rate has been updated. \[commission_rate\] + CommissionRateUpdated(Rate), } /// The current era of relaychain @@ -272,6 +274,14 @@ pub mod module { #[pallet::getter(fn redeem_threshold)] pub type RedeemThreshold = StorageValue<_, Balance, ValueQuery>; + /// The rate of Homa drawn from the staking reward as commision. + /// The draw will be transfer to TreasuryAccount of Homa in liquid currency. + /// + /// CommissionRate: value: Rate + #[pallet::storage] + #[pallet::getter(fn commission_rate)] + pub type CommissionRate = StorageValue<_, Rate, ValueQuery>; + #[pallet::pallet] pub struct Pallet(_); @@ -452,7 +462,6 @@ pub mod module { RelayChainCurrentEra::::try_mutate(|current_era| -> DispatchResult { ensure!(new_era > *current_era, Error::::InvalidEraIndex); - *current_era = new_era; // reset void liquid to zero firstly, to guarantee TotalVoidLiquid::::put(0); @@ -460,10 +469,14 @@ pub mod module { // TODO: consider execute rebalance on on_idle, before the processing is completed, // the mint and request_redeem functions should be unavailable. // Rebalance: + Self::draw_staking_reward(new_era, *current_era)?; Self::process_scheduled_unbond(new_era)?; Self::process_to_bond_pool(new_era)?; Self::process_redeem_requests(new_era)?; + // bump current era to latest. + *current_era = new_era; + Self::deposit_event(Event::::CurrentEraBumped(new_era)); Ok(()) }) @@ -513,6 +526,7 @@ pub mod module { estimated_reward_rate_per_era: Option, mint_threshold: Option, redeem_threshold: Option, + commission_rate: Option, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; @@ -532,6 +546,10 @@ pub mod module { RedeemThreshold::::put(threshold); Self::deposit_event(Event::::RedeemThresholdUpdated(threshold)); } + if let Some(rate) = commission_rate { + CommissionRate::::put(rate); + Self::deposit_event(Event::::CommissionRateUpdated(rate)); + } Ok(()) } @@ -580,13 +598,18 @@ pub mod module { .saturating_mul(T::ActiveSubAccountsIndexList::get().len() as Balance) } - /// Calculate the total amount of staking currency belong to Homa. - pub fn get_total_staking_currency() -> Balance { - StakingLedgers::::iter().fold(Self::to_bond_pool(), |total_bonded, (_, ledger)| { + /// Calculate the total amount of bonded staking currency. + pub fn get_total_bonded() -> Balance { + StakingLedgers::::iter().fold(Zero::zero(), |total_bonded, (_, ledger)| { total_bonded.saturating_add(ledger.bonded) }) } + /// Calculate the total amount of staking currency belong to Homa. + pub fn get_total_staking_currency() -> Balance { + Self::get_total_bonded().saturating_add(Self::to_bond_pool()) + } + /// Calculate the current exchange rate between the staking currency and liquid currency. /// Note: ExchangeRate(staking : liquid) = total_staking_amount / (liquid_total_issuance + /// total_void_liquid) If the exchange rate cannot be calculated, T::DefaultExchangeRate is @@ -649,11 +672,18 @@ pub mod module { .saturating_mul_int(actual_liquid_to_redeem); let redeemed_staking = Self::convert_liquid_to_staking(liquid_to_burn)?; let fee_in_liquid = actual_liquid_to_redeem.saturating_sub(liquid_to_burn); + let liquid_currency_id = T::LiquidCurrencyId::get(); - // TODO: record the fee_in_liquid reward it to HomaTreasury as benifit. + // burn liquid_to_burn. + T::Currency::withdraw(liquid_currency_id, &module_account, liquid_to_burn)?; - // burn liquid_to_burn - T::Currency::withdraw(T::LiquidCurrencyId::get(), &module_account, liquid_to_burn)?; + // transfer fee_in_liquid to TreasuryAccount of Homa. + T::Currency::transfer( + liquid_currency_id, + &module_account, + &T::TreasuryAccount::get(), + fee_in_liquid, + )?; // transfer redeemed_staking to redeemer. T::Currency::transfer( @@ -683,6 +713,31 @@ pub mod module { }) } + #[transactional] + pub fn draw_staking_reward(new_era: EraIndex, previous_era: EraIndex) -> DispatchResult { + let era_interval = new_era.saturating_sub(previous_era); + let liquid_currency_id = T::LiquidCurrencyId::get(); + let draw_rate = Ratio::checked_from_rational(Self::get_total_bonded(), Self::get_total_staking_currency()) + .unwrap_or_else(Ratio::zero) + .saturating_mul(Self::estimated_reward_rate_per_era()) + .saturating_add(Rate::one()) + .saturating_pow(era_interval.unique_saturated_into()) + .saturating_sub(Rate::one()) + .saturating_mul(Self::commission_rate()); + let inflation_rate = Rate::one() + .saturating_add(draw_rate) + .reciprocal() + .expect("shouldn't be invalid!"); + + let liquid_amount_as_commision = + inflation_rate.saturating_mul_int(T::Currency::total_issuance(liquid_currency_id)); + T::Currency::deposit( + liquid_currency_id, + &T::TreasuryAccount::get(), + liquid_amount_as_commision, + ) + } + /// Get back unbonded of all subaccounts on relaychain by XCM. /// The staking currency withdrew becomes available to be redeemed. #[transactional] From 2c5e6c3b3c7dd8f436dcffc3d381d573c00bf145 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Wed, 1 Dec 2021 15:49:56 +0800 Subject: [PATCH 05/27] update --- modules/homa/src/lib.rs | 92 +++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index cd2b001ab6..cc7e9c2f7f 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -147,6 +147,8 @@ pub mod module { InsufficientUnclaimedRedemption, /// Invalid era index to bump, must be greater than RelayChainCurrentEra InvalidEraIndex, + /// Redeem request is not allowed to be fast matched. + FastMatchIsNotAllowed, } #[pallet::event] @@ -155,13 +157,16 @@ pub mod module { /// The minter use staking currency to mint liquid currency. \[minter, /// staking_currency_amount, liquid_currency_amount_received\] Minted(T::AccountId, Balance, Balance), - /// Request redeem. \[redeemer, liquid_amount, fast_match_fee_rate\] - RequestedRedeem(T::AccountId, Balance, Rate), + /// Request redeem. \[redeemer, liquid_amount, allow_fast_match\] + RequestedRedeem(T::AccountId, Balance, bool), /// Redeem request has been cancelled. \[redeemer, cancelled_liquid_amount\] RedeemRequestCancelled(T::AccountId, Balance), /// Redeem request is redeemed partially or fully by fast match. \[redeemer, /// matched_liquid_amount, fee_in_liquid, redeemed_staking_amount\] RedeemedByFastMatch(T::AccountId, Balance, Balance, Balance), + /// Redeem request is redeemed by unbond on relaychain. \[redeemer, + /// era_index_when_unbond, liquid_amount, unbonding_staking_amount\] + RedeemedByUnbond(T::AccountId, EraIndex, Balance, Balance), /// The redeemer withdraw expired redemption. \[redeemer, redeption_amount\] WithdrawRedemption(T::AccountId, Balance), /// The current era has been bumped. \[new_era_index\] @@ -183,6 +188,8 @@ pub mod module { RedeemThresholdUpdated(Balance), /// The commission rate has been updated. \[commission_rate\] CommissionRateUpdated(Rate), + /// The fast match fee rate has been updated. \[commission_rate\] + FastMatchFeeRateUpdated(Rate), } /// The current era of relaychain @@ -232,10 +239,11 @@ pub mod module { pub type UnclaimedRedemption = StorageValue<_, Balance, ValueQuery>; /// Requests to redeem staked currencies. - /// RedeemRequests: Map: AccountId => Option<(liquid_amount: Balance, addtional_fee: Rate)> + /// + /// RedeemRequests: Map: AccountId => Option<(liquid_amount: Balance, allow_fast_match: bool)> #[pallet::storage] #[pallet::getter(fn redeem_requests)] - pub type RedeemRequests = StorageMap<_, Twox64Concat, T::AccountId, (Balance, Rate), OptionQuery>; + pub type RedeemRequests = StorageMap<_, Twox64Concat, T::AccountId, (Balance, bool), OptionQuery>; /// The records of unbonding by AccountId. /// @@ -282,6 +290,13 @@ pub mod module { #[pallet::getter(fn commission_rate)] pub type CommissionRate = StorageValue<_, Rate, ValueQuery>; + /// The fixed fee rate for redeem request is fast matched. + /// + /// FastMatchFeeRate: value: Rate + #[pallet::storage] + #[pallet::getter(fn fast_match_fee_rate)] + pub type FastMatchFeeRate = StorageValue<_, Rate, ValueQuery>; + #[pallet::pallet] pub struct Pallet(_); @@ -339,13 +354,14 @@ pub mod module { /// Parameters: /// - `amount`: The amount of liquid currency to be requested redeemed into Staking /// currency. - /// - `fast_match_fee_rate`: Fee rate for pay fast match. + /// - `allow_fast_match`: allow the request to be fast matched, fast match will take a fixed + /// rate as fee. #[pallet::weight(10_000)] #[transactional] pub fn request_redeem( origin: OriginFor, #[pallet::compact] amount: Balance, - fast_match_fee_rate: Rate, + allow_fast_match: bool, ) -> DispatchResult { let redeemer = ensure_signed(origin)?; @@ -383,19 +399,13 @@ pub mod module { }?; if !amount.is_zero() { - *maybe_request = Some((amount, fast_match_fee_rate)); - Self::deposit_event(Event::::RequestedRedeem( + *maybe_request = Some((amount, allow_fast_match)); + Self::deposit_event(Event::::RequestedRedeem(redeemer.clone(), amount, allow_fast_match)); + } else if !previous_request_amount.is_zero() { + Self::deposit_event(Event::::RedeemRequestCancelled( redeemer.clone(), - amount, - fast_match_fee_rate, + previous_request_amount, )); - } else { - if !previous_request_amount.is_zero() { - Self::deposit_event(Event::::RedeemRequestCancelled( - redeemer.clone(), - previous_request_amount, - )); - } } Ok(()) }) @@ -527,6 +537,7 @@ pub mod module { mint_threshold: Option, redeem_threshold: Option, commission_rate: Option, + fast_match_fee_rate: Option, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; @@ -550,6 +561,10 @@ pub mod module { CommissionRate::::put(rate); Self::deposit_event(Event::::CommissionRateUpdated(rate)); } + if let Some(rate) = fast_match_fee_rate { + FastMatchFeeRate::::put(rate); + Self::deposit_event(Event::::FastMatchFeeRateUpdated(rate)); + } Ok(()) } @@ -647,12 +662,15 @@ pub mod module { #[transactional] pub fn do_fast_match_redeem(redeemer: &T::AccountId) -> DispatchResult { RedeemRequests::::try_mutate_exists(redeemer, |maybe_request| -> DispatchResult { - if let Some((request_amount, fee_rate)) = maybe_request.take() { + if let Some((request_amount, allow_fast_match)) = maybe_request.take() { + ensure!(allow_fast_match, Error::::FastMatchIsNotAllowed); + // calculate the liquid currency limit can be used to redeem based on ToBondPool at fee_rate. let available_staking_currency = Self::to_bond_pool(); let liquid_currency_limit = Self::convert_staking_to_liquid(available_staking_currency)?; + let fast_match_fee_rate = Self::fast_match_fee_rate(); let liquid_limit_at_fee_rate = Rate::one() - .saturating_sub(fee_rate) + .saturating_sub(fast_match_fee_rate) .reciprocal() .unwrap_or_else(Bounded::max_value) .saturating_mul_int(liquid_currency_limit); @@ -668,22 +686,14 @@ pub mod module { if !actual_liquid_to_redeem.is_zero() { let liquid_to_burn = Rate::one() - .saturating_sub(fee_rate) + .saturating_sub(fast_match_fee_rate) .saturating_mul_int(actual_liquid_to_redeem); let redeemed_staking = Self::convert_liquid_to_staking(liquid_to_burn)?; let fee_in_liquid = actual_liquid_to_redeem.saturating_sub(liquid_to_burn); - let liquid_currency_id = T::LiquidCurrencyId::get(); - - // burn liquid_to_burn. - T::Currency::withdraw(liquid_currency_id, &module_account, liquid_to_burn)?; - // transfer fee_in_liquid to TreasuryAccount of Homa. - T::Currency::transfer( - liquid_currency_id, - &module_account, - &T::TreasuryAccount::get(), - fee_in_liquid, - )?; + // burn liquid_to_burn for redeemed_staking and burn fee_in_liquid to reward all holders of + // liquid currency. + T::Currency::withdraw(T::LiquidCurrencyId::get(), &module_account, actual_liquid_to_redeem)?; // transfer redeemed_staking to redeemer. T::Currency::transfer( @@ -705,7 +715,7 @@ pub mod module { // update request amount let remainer_request_amount = request_amount.saturating_sub(actual_liquid_to_redeem); if !remainer_request_amount.is_zero() { - *maybe_request = Some((remainer_request_amount, fee_rate)); + *maybe_request = Some((remainer_request_amount, allow_fast_match)); } } @@ -713,12 +723,18 @@ pub mod module { }) } + /// Draw commission to TreasuryAccount from estimated staking rewards. Commission will be + /// given to TreasuryAccount by issuing liquid currency. Note: This will cause some losses + /// to the minters in previous_era, because they have been already deducted some liquid + /// currency amount when mint in previous_era. Until there is a better way to calculate, + /// this part of the loss can only be regarded as an implicit mint fee! #[transactional] pub fn draw_staking_reward(new_era: EraIndex, previous_era: EraIndex) -> DispatchResult { let era_interval = new_era.saturating_sub(previous_era); let liquid_currency_id = T::LiquidCurrencyId::get(); - let draw_rate = Ratio::checked_from_rational(Self::get_total_bonded(), Self::get_total_staking_currency()) - .unwrap_or_else(Ratio::zero) + let bond_ratio = Ratio::checked_from_rational(Self::get_total_bonded(), Self::get_total_staking_currency()) + .unwrap_or_else(Ratio::zero); + let draw_rate = bond_ratio .saturating_mul(Self::estimated_reward_rate_per_era()) .saturating_add(Rate::one()) .saturating_pow(era_interval.unique_saturated_into()) @@ -836,7 +852,13 @@ pub mod module { for (redeemer, (redeem_amount, _)) in RedeemRequests::::drain() { total_redeem_amount = total_redeem_amount.saturating_add(redeem_amount); let redemption_amount = Self::convert_liquid_to_staking(redeem_amount)?; - Unbondings::::insert(redeemer, era_index_to_expire, redemption_amount); + Unbondings::::insert(&redeemer, era_index_to_expire, redemption_amount); + Self::deposit_event(Event::::RedeemedByUnbond( + redeemer, + new_era, + redeem_amount, + redemption_amount, + )); } // calculate the distribution for unbond From dcf66aa088f0ad599c9e9f858ac0c036b473f788 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Wed, 1 Dec 2021 15:53:28 +0800 Subject: [PATCH 06/27] update comments --- modules/homa/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index cc7e9c2f7f..eba2277b8a 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -527,7 +527,12 @@ pub mod module { /// - `soft_bonded_cap_per_sub_account`: soft cap of staking amount for a single nominator /// on relaychain to obtain the best staking rewards. /// - `estimated_reward_rate_per_era`: the esstaking yield of each era on the current relay - /// chain + /// chain. + /// - `mint_threshold`: the staking currency amount of threshold when mint. + /// - `redeem_threshold`: the liquid currency amount of threshold when request redeem. + /// - `commission_rate`: the rate to draw from estimated staking rewards as commission to + /// HomaTreasury + /// - `fast_match_fee_rate`: the fixed fee rate when redeem request is been fast matched. #[pallet::weight(10_000)] #[transactional] pub fn update_homa_params( From 8804eeab1bf69deaf43cb6c36636b48e3320be98 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Wed, 1 Dec 2021 22:37:16 +0800 Subject: [PATCH 07/27] add some tests --- modules/homa/src/lib.rs | 12 +- modules/homa/src/tests.rs | 291 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+), 6 deletions(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index eba2277b8a..1010acc454 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -58,10 +58,10 @@ pub mod module { pub struct UnlockChunk { /// Amount of funds to be unlocked. #[codec(compact)] - value: Balance, + pub value: Balance, /// Era number at which point it'll be unlocked. #[codec(compact)] - era: EraIndex, + pub era: EraIndex, } impl StakingLedger { @@ -918,16 +918,16 @@ pub fn distribute_increment( amount_list.sort_by(|a, b| a.1.cmp(&b.1)); for (index, amount) in amount_list { - if remain_increment.is_zero() || remain_increment < minimum_increment.unwrap_or_else(Bounded::max_value) { + if remain_increment.is_zero() || remain_increment < minimum_increment.unwrap_or_else(Bounded::min_value) { break; } let increment_distribution = amount_cap .unwrap_or_else(Bounded::max_value) - .saturating_add(amount) + .saturating_sub(amount) .min(remain_increment); if increment_distribution.is_zero() - || increment_distribution < minimum_increment.unwrap_or_else(Bounded::max_value) + || increment_distribution < minimum_increment.unwrap_or_else(Bounded::min_value) { continue; } @@ -951,7 +951,7 @@ pub fn distribute_decrement( amount_list.sort_by(|a, b| b.1.cmp(&a.1)); for (index, amount) in amount_list { - if remain_decrement.is_zero() || remain_decrement < minimum_decrement.unwrap_or_else(Bounded::max_value) { + if remain_decrement.is_zero() || remain_decrement < minimum_decrement.unwrap_or_else(Bounded::min_value) { break; } diff --git a/modules/homa/src/tests.rs b/modules/homa/src/tests.rs index 8447de0b69..c7f591d011 100644 --- a/modules/homa/src/tests.rs +++ b/modules/homa/src/tests.rs @@ -17,3 +17,294 @@ // along with this program. If not, see . //! Unit tests for the Homa Module + +#![cfg(test)] + +use super::*; +use frame_support::{assert_noop, assert_ok}; +use mock::{Event, *}; +use orml_traits::MultiCurrency; +use sp_runtime::{traits::BadOrigin, FixedPointNumber}; + +#[test] +fn update_ledgers_work() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!(Homa::update_ledgers(Origin::signed(ALICE), vec![]), BadOrigin); + + assert_eq!(Homa::staking_ledgers(0), None); + assert_eq!(Homa::staking_ledgers(1), None); + + assert_ok!(Homa::update_ledgers( + Origin::signed(HomaAdmin::get()), + vec![ + ( + 0, + Some(1_000_000), + Some(vec![ + UnlockChunk { value: 1000, era: 5 }, + UnlockChunk { value: 20_000, era: 6 }, + ]) + ), + (1, None, Some(vec![UnlockChunk { value: 2000, era: 10 },])), + ] + )); + System::assert_has_event(Event::Homa(crate::Event::LedgerBondedUpdated(0, 1_000_000))); + System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingUpdated( + 0, + vec![ + UnlockChunk { value: 1000, era: 5 }, + UnlockChunk { value: 20_000, era: 6 }, + ], + ))); + System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingUpdated( + 1, + vec![UnlockChunk { value: 2000, era: 10 }], + ))); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![ + UnlockChunk { value: 1000, era: 5 }, + UnlockChunk { value: 20_000, era: 6 }, + ] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 0, + unlocking: vec![UnlockChunk { value: 2000, era: 10 },] + }) + ); + + assert_ok!(Homa::update_ledgers( + Origin::signed(HomaAdmin::get()), + vec![ + (0, None, Some(vec![UnlockChunk { value: 20_000, era: 6 },])), + (1, Some(0), Some(vec![])), + ] + )); + System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingUpdated( + 0, + vec![UnlockChunk { value: 20_000, era: 6 }], + ))); + System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingUpdated(1, vec![]))); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![UnlockChunk { value: 20_000, era: 6 },] + }) + ); + assert_eq!(Homa::staking_ledgers(1), None); + }); +} + +#[test] +fn update_homa_params_work() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Homa::update_homa_params(Origin::signed(ALICE), None, None, None, None, None, None), + BadOrigin + ); + + assert_eq!(Homa::soft_bonded_cap_per_sub_account(), 0); + assert_eq!(Homa::estimated_reward_rate_per_era(), Rate::zero()); + assert_eq!(Homa::mint_threshold(), 0); + assert_eq!(Homa::redeem_threshold(), 0); + assert_eq!(Homa::commission_rate(), Rate::zero()); + assert_eq!(Homa::fast_match_fee_rate(), Rate::zero()); + + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + Some(dollar(10000)), + Some(Rate::saturating_from_rational(1, 10000)), + Some(dollar(1)), + Some(dollar(10)), + Some(Rate::saturating_from_rational(5, 100)), + Some(Rate::saturating_from_rational(1, 100)), + )); + System::assert_has_event(Event::Homa(crate::Event::SoftBondedCapPerSubAccountUpdated(dollar( + 10000, + )))); + System::assert_has_event(Event::Homa(crate::Event::EstimatedRewardRatePerEraUpdated( + Rate::saturating_from_rational(1, 10000), + ))); + System::assert_has_event(Event::Homa(crate::Event::MintThresholdUpdated(dollar(1)))); + System::assert_has_event(Event::Homa(crate::Event::RedeemThresholdUpdated(dollar(10)))); + System::assert_has_event(Event::Homa(crate::Event::CommissionRateUpdated( + Rate::saturating_from_rational(5, 100), + ))); + System::assert_has_event(Event::Homa(crate::Event::FastMatchFeeRateUpdated( + Rate::saturating_from_rational(1, 100), + ))); + assert_eq!(Homa::soft_bonded_cap_per_sub_account(), dollar(10000)); + assert_eq!( + Homa::estimated_reward_rate_per_era(), + Rate::saturating_from_rational(1, 10000) + ); + assert_eq!(Homa::mint_threshold(), dollar(1)); + assert_eq!(Homa::redeem_threshold(), dollar(10)); + assert_eq!(Homa::commission_rate(), Rate::saturating_from_rational(5, 100)); + assert_eq!(Homa::fast_match_fee_rate(), Rate::saturating_from_rational(1, 100)); + }); +} + +#[test] +fn get_staking_currency_soft_cap_work() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Homa::get_staking_currency_soft_cap(), 0); + SoftBondedCapPerSubAccount::::put(1_000_000); + assert_eq!( + Homa::get_staking_currency_soft_cap(), + 1_000_000 * (ActiveSubAccountsIndexList::get().len() as Balance) + ); + }); +} + +#[test] +fn get_total_bonded_soft_cap_work() { + ExtBuilder::default().build().execute_with(|| { + StakingLedgers::::insert( + 0, + StakingLedger { + bonded: 1_000_000, + ..Default::default() + }, + ); + StakingLedgers::::insert( + 1, + StakingLedger { + bonded: 2_000_000, + ..Default::default() + }, + ); + StakingLedgers::::insert( + 3, + StakingLedger { + bonded: 1_000_000, + ..Default::default() + }, + ); + assert_eq!(Homa::get_total_bonded(), 4_000_000); + }); +} + +#[test] +fn get_total_staking_currency_work() { + ExtBuilder::default().build().execute_with(|| { + StakingLedgers::::insert( + 0, + StakingLedger { + bonded: 1_000_000, + ..Default::default() + }, + ); + StakingLedgers::::insert( + 1, + StakingLedger { + bonded: 2_000_000, + ..Default::default() + }, + ); + ToBondPool::::put(2_000_000); + assert_eq!(Homa::get_total_staking_currency(), 5_000_000); + }); +} + +#[test] +fn current_exchange_rate_work() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_000_000)); + assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(10_000_000)); + + StakingLedgers::::insert( + 0, + StakingLedger { + bonded: 1_000_000, + ..Default::default() + }, + ); + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_000_000)); + assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(10_000_000)); + + assert_ok!(Currencies::deposit(LiquidCurrencyId::get(), &ALICE, 5_000_000)); + assert_eq!( + Homa::current_exchange_rate(), + ExchangeRate::saturating_from_rational(1_000_000, 5_000_000) + ); + assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(2_000_000)); + assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(5_000_000)); + + TotalVoidLiquid::::put(3_000_000); + assert_eq!( + Homa::current_exchange_rate(), + ExchangeRate::saturating_from_rational(1_000_000, 8_000_000) + ); + assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_250_000)); + assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(8_000_000)); + }); +} + +#[test] +fn distribution_helpers_work() { + ExtBuilder::default().build().execute_with(|| { + let bonded_list = vec![(0, 1_000_000), (1, 2_000_000), (2, 3_000_000), (3, 100_000)]; + + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, None, None), + (vec![(3, 2_000_000)], 0) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, Some(1_100_000), None), + (vec![(3, 1_000_000), (0, 100_000)], 900_000) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, Some(100_000), None), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, None, Some(2_000_001)), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, Some(1_000_000), Some(900_001)), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, Some(1_200_000), Some(1_000_000)), + (vec![(3, 1_100_000)], 900_000) + ); + + assert_eq!( + distribute_decrement(bonded_list.clone(), 7_000_000, None, None), + ( + vec![(2, 3_000_000), (1, 2_000_000), (0, 1_000_000), (3, 100_000)], + 900_000 + ) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 2_000_000, Some(1_800_000), None), + (vec![(2, 1_200_000), (1, 200_000)], 600_000) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 2_000_000, Some(3_000_000), None), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 6_000_000, None, Some(2_000_000)), + (vec![(2, 3_000_000), (1, 2_000_000)], 1_000_000) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 2_000_000, None, Some(3_000_001)), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 3_000_000, Some(1_000_000), Some(1_000_001)), + (vec![(2, 2_000_000)], 1_000_000) + ); + }); +} From 5d90cce0d4ae416d95a531677948888174c9b787 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Fri, 3 Dec 2021 14:46:58 +0800 Subject: [PATCH 08/27] fix and add some tests --- modules/homa/src/lib.rs | 167 ++++--- modules/homa/src/mock.rs | 12 +- modules/homa/src/tests.rs | 995 +++++++++++++++++++++++++++++++++++++- 3 files changed, 1072 insertions(+), 102 deletions(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 1010acc454..34dc74598d 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -29,7 +29,7 @@ use pallet_staking::EraIndex; use primitives::{Balance, CurrencyId}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{AccountIdConversion, Bounded, One, Saturating, UniqueSaturatedInto, Zero}, + traits::{AccountIdConversion, Bounded, CheckedDiv, One, Saturating, UniqueSaturatedInto, Zero}, ArithmeticError, FixedPointNumber, }; use sp_std::{cmp::Ordering, convert::From, prelude::*, vec, vec::Vec}; @@ -145,8 +145,8 @@ pub mod module { ExceededStakingCurrencySoftCap, /// UnclaimedRedemption is not enough, this error is not expected. InsufficientUnclaimedRedemption, - /// Invalid era index to bump, must be greater than RelayChainCurrentEra - InvalidEraIndex, + /// The era index to bump is outdated, must be greater than RelayChainCurrentEra + OutdatedEraIndex, /// Redeem request is not allowed to be fast matched. FastMatchIsNotAllowed, } @@ -155,8 +155,8 @@ pub mod module { #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { /// The minter use staking currency to mint liquid currency. \[minter, - /// staking_currency_amount, liquid_currency_amount_received\] - Minted(T::AccountId, Balance, Balance), + /// staking_currency_amount, liquid_amount_received, liquid_amount_added_to_void\] + Minted(T::AccountId, Balance, Balance, Balance), /// Request redeem. \[redeemer, liquid_amount, allow_fast_match\] RequestedRedeem(T::AccountId, Balance, bool), /// Redeem request has been cancelled. \[redeemer, cancelled_liquid_amount\] @@ -338,7 +338,12 @@ pub mod module { ToBondPool::::mutate(|pool| *pool = pool.saturating_add(amount)); TotalVoidLiquid::::mutate(|total| *total = total.saturating_add(liquid_add_to_void)); - Self::deposit_event(Event::::Minted(minter, amount, liquid_issue_to_minter)); + Self::deposit_event(Event::::Minted( + minter, + amount, + liquid_issue_to_minter, + liquid_add_to_void, + )); Ok(()) } @@ -375,9 +380,8 @@ pub mod module { ); match amount.cmp(&previous_request_amount) { - Ordering::Greater => - // pay more liquid currency. - { + Ordering::Greater => { + // pay more liquid currency. T::Currency::transfer( liquid_currency_id, &redeemer, @@ -385,9 +389,8 @@ pub mod module { amount.saturating_sub(previous_request_amount), ) } - Ordering::Less => - // refund the difference. - { + Ordering::Less => { + // refund the difference. T::Currency::transfer( liquid_currency_id, &Self::account_id(), @@ -437,26 +440,31 @@ pub mod module { let _ = ensure_signed(origin)?; let mut available_staking: Balance = Zero::zero(); - Unbondings::::iter_prefix(&redeemer) - .filter(|(era_index, _)| era_index <= &Self::relay_chain_current_era()) - .for_each(|(expired_era_index, unbonded)| { + let current_era = Self::relay_chain_current_era(); + for (expired_era_index, unbonded) in Unbondings::::iter_prefix(&redeemer) { + if expired_era_index <= current_era { available_staking = available_staking.saturating_add(unbonded); Unbondings::::remove(&redeemer, expired_era_index); - }); - UnclaimedRedemption::::try_mutate(|total| -> DispatchResult { - *total = total - .checked_sub(available_staking) - .ok_or(Error::::InsufficientUnclaimedRedemption)?; - Ok(()) - })?; - T::Currency::transfer( - T::StakingCurrencyId::get(), - &Self::account_id(), - &redeemer, - available_staking, - )?; + } + } + + if !available_staking.is_zero() { + UnclaimedRedemption::::try_mutate(|total| -> DispatchResult { + *total = total + .checked_sub(available_staking) + .ok_or(Error::::InsufficientUnclaimedRedemption)?; + Ok(()) + })?; + T::Currency::transfer( + T::StakingCurrencyId::get(), + &Self::account_id(), + &redeemer, + available_staking, + )?; + + Self::deposit_event(Event::::WithdrawRedemption(redeemer, available_staking)); + } - Self::deposit_event(Event::::WithdrawRedemption(redeemer, available_staking)); Ok(()) } @@ -471,7 +479,7 @@ pub mod module { T::GovernanceOrigin::ensure_origin(origin)?; RelayChainCurrentEra::::try_mutate(|current_era| -> DispatchResult { - ensure!(new_era > *current_era, Error::::InvalidEraIndex); + ensure!(new_era > *current_era, Error::::OutdatedEraIndex); // reset void liquid to zero firstly, to guarantee TotalVoidLiquid::::put(0); @@ -739,17 +747,15 @@ pub mod module { let liquid_currency_id = T::LiquidCurrencyId::get(); let bond_ratio = Ratio::checked_from_rational(Self::get_total_bonded(), Self::get_total_staking_currency()) .unwrap_or_else(Ratio::zero); - let draw_rate = bond_ratio + let reward_rate = bond_ratio .saturating_mul(Self::estimated_reward_rate_per_era()) .saturating_add(Rate::one()) .saturating_pow(era_interval.unique_saturated_into()) - .saturating_sub(Rate::one()) - .saturating_mul(Self::commission_rate()); - let inflation_rate = Rate::one() - .saturating_add(draw_rate) - .reciprocal() + .saturating_sub(Rate::one()); + let commission_rate = reward_rate.saturating_mul(Self::commission_rate()); + let inflation_rate = commission_rate + .checked_div(&Rate::one().saturating_add(reward_rate).saturating_sub(commission_rate)) .expect("shouldn't be invalid!"); - let liquid_amount_as_commision = inflation_rate.saturating_mul_int(T::Currency::total_issuance(liquid_currency_id)); T::Currency::deposit( @@ -808,36 +814,42 @@ pub mod module { new_era ); - let xcm_transfer_fee = T::HomaXcm::get_xcm_transfer_fee(); - let bonded_list: Vec<(u16, Balance)> = T::ActiveSubAccountsIndexList::get() - .iter() - .map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded)) - .collect(); - let (distribution, remainer) = distribute_increment::( - bonded_list, - Self::to_bond_pool(), - Some(Self::soft_bonded_cap_per_sub_account()), - Some(xcm_transfer_fee), - ); + let to_bond_pool = Self::to_bond_pool(); + + // if to_bond is gte than mint_threshold, try to bond_extra on relaychain + if to_bond_pool >= Self::mint_threshold() { + let xcm_transfer_fee = T::HomaXcm::get_xcm_transfer_fee(); + let bonded_list: Vec<(u16, Balance)> = T::ActiveSubAccountsIndexList::get() + .iter() + .map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded)) + .collect(); + let (distribution, remainer) = distribute_increment::( + bonded_list, + to_bond_pool, + Some(Self::soft_bonded_cap_per_sub_account().saturating_add(xcm_transfer_fee)), + Some(xcm_transfer_fee), + ); - // subaccounts execute the distribution - for (sub_account_index, amount) in distribution { - if !amount.is_zero() { - T::HomaXcm::transfer_staking_to_sub_account(&Self::account_id(), sub_account_index, amount)?; + // subaccounts execute the distribution + for (sub_account_index, amount) in distribution { + if !amount.is_zero() { + T::HomaXcm::transfer_staking_to_sub_account(&Self::account_id(), sub_account_index, amount)?; - let bond_amount = amount.saturating_sub(xcm_transfer_fee); - T::HomaXcm::bond_extra_on_sub_account(sub_account_index, bond_amount)?; + let bond_amount = amount.saturating_sub(xcm_transfer_fee); + T::HomaXcm::bond_extra_on_sub_account(sub_account_index, bond_amount)?; - // udpate ledger - Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { - ledger.bonded = ledger.bonded.saturating_add(bond_amount); - Ok(()) - })?; + // udpate ledger + Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { + ledger.bonded = ledger.bonded.saturating_add(bond_amount); + Ok(()) + })?; + } } + + // update pool + ToBondPool::::mutate(|pool| *pool = remainer); } - // update pool - ToBondPool::::mutate(|pool| *pool = remainer); Ok(()) } @@ -850,24 +862,33 @@ pub mod module { new_era ); - let mut total_redeem_amount: Balance = Zero::zero(); let era_index_to_expire = new_era + T::BondingDuration::get(); + let total_bonded = Self::get_total_bonded(); + let mut total_redeem_amount: Balance = Zero::zero(); + let mut remain_total_bonded = total_bonded; - // drain RedeemRequests and insert to Unbondings - for (redeemer, (redeem_amount, _)) in RedeemRequests::::drain() { - total_redeem_amount = total_redeem_amount.saturating_add(redeem_amount); + // iter RedeemRequests and insert to Unbondings if remain_total_bonded is enough. + for (redeemer, (redeem_amount, _)) in RedeemRequests::::iter() { let redemption_amount = Self::convert_liquid_to_staking(redeem_amount)?; - Unbondings::::insert(&redeemer, era_index_to_expire, redemption_amount); - Self::deposit_event(Event::::RedeemedByUnbond( - redeemer, - new_era, - redeem_amount, - redemption_amount, - )); + + if remain_total_bonded >= redemption_amount { + total_redeem_amount = total_redeem_amount.saturating_add(redeem_amount); + remain_total_bonded = remain_total_bonded.saturating_sub(redemption_amount); + RedeemRequests::::remove(&redeemer); + Unbondings::::insert(&redeemer, era_index_to_expire, redemption_amount); + Self::deposit_event(Event::::RedeemedByUnbond( + redeemer, + new_era, + redeem_amount, + redemption_amount, + )); + } else { + break; + } } // calculate the distribution for unbond - let staking_amount_to_unbond = Self::convert_liquid_to_staking(total_redeem_amount)?; + let staking_amount_to_unbond = total_bonded.saturating_sub(remain_total_bonded); let bonded_list: Vec<(u16, Balance)> = T::ActiveSubAccountsIndexList::get() .iter() .map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded)) diff --git a/modules/homa/src/mock.rs b/modules/homa/src/mock.rs index 6c5cee597f..44bddfa4d1 100644 --- a/modules/homa/src/mock.rs +++ b/modules/homa/src/mock.rs @@ -48,16 +48,6 @@ pub const NATIVE_CURRENCY_ID: CurrencyId = CurrencyId::Token(TokenSymbol::ACA); pub const STAKING_CURRENCY_ID: CurrencyId = CurrencyId::Token(TokenSymbol::DOT); pub const LIQUID_CURRENCY_ID: CurrencyId = CurrencyId::Token(TokenSymbol::LDOT); -/// For testing only. Does not check for overflow. -pub fn dollar(b: Balance) -> Balance { - b * 1_000_000_000_000 -} - -/// For testing only. Does not check for overflow. -pub fn cent(b: Balance) -> Balance { - b * 10_000_000_000 -} - /// mock XCM transfer. pub struct MockHomaSubAccountXcm; impl HomaSubAccountXcm for MockHomaSubAccountXcm { @@ -78,7 +68,7 @@ impl HomaSubAccountXcm for MockHomaSubAccountXcm { } fn get_xcm_transfer_fee() -> Balance { - cent(10) + 1_000_000 } } diff --git a/modules/homa/src/tests.rs b/modules/homa/src/tests.rs index c7f591d011..fd279a9e0c 100644 --- a/modules/homa/src/tests.rs +++ b/modules/homa/src/tests.rs @@ -27,7 +27,7 @@ use orml_traits::MultiCurrency; use sp_runtime::{traits::BadOrigin, FixedPointNumber}; #[test] -fn update_ledgers_work() { +fn update_ledgers_works() { ExtBuilder::default().build().execute_with(|| { assert_noop!(Homa::update_ledgers(Origin::signed(ALICE), vec![]), BadOrigin); @@ -102,7 +102,7 @@ fn update_ledgers_work() { } #[test] -fn update_homa_params_work() { +fn update_homa_params_works() { ExtBuilder::default().build().execute_with(|| { assert_noop!( Homa::update_homa_params(Origin::signed(ALICE), None, None, None, None, None, None), @@ -118,41 +118,41 @@ fn update_homa_params_work() { assert_ok!(Homa::update_homa_params( Origin::signed(HomaAdmin::get()), - Some(dollar(10000)), + Some(1_000_000_000), Some(Rate::saturating_from_rational(1, 10000)), - Some(dollar(1)), - Some(dollar(10)), + Some(1_000_000), + Some(10_000_000), Some(Rate::saturating_from_rational(5, 100)), Some(Rate::saturating_from_rational(1, 100)), )); - System::assert_has_event(Event::Homa(crate::Event::SoftBondedCapPerSubAccountUpdated(dollar( - 10000, - )))); + System::assert_has_event(Event::Homa(crate::Event::SoftBondedCapPerSubAccountUpdated( + 1_000_000_000, + ))); System::assert_has_event(Event::Homa(crate::Event::EstimatedRewardRatePerEraUpdated( Rate::saturating_from_rational(1, 10000), ))); - System::assert_has_event(Event::Homa(crate::Event::MintThresholdUpdated(dollar(1)))); - System::assert_has_event(Event::Homa(crate::Event::RedeemThresholdUpdated(dollar(10)))); + System::assert_has_event(Event::Homa(crate::Event::MintThresholdUpdated(1_000_000))); + System::assert_has_event(Event::Homa(crate::Event::RedeemThresholdUpdated(10_000_000))); System::assert_has_event(Event::Homa(crate::Event::CommissionRateUpdated( Rate::saturating_from_rational(5, 100), ))); System::assert_has_event(Event::Homa(crate::Event::FastMatchFeeRateUpdated( Rate::saturating_from_rational(1, 100), ))); - assert_eq!(Homa::soft_bonded_cap_per_sub_account(), dollar(10000)); + assert_eq!(Homa::soft_bonded_cap_per_sub_account(), 1_000_000_000); assert_eq!( Homa::estimated_reward_rate_per_era(), Rate::saturating_from_rational(1, 10000) ); - assert_eq!(Homa::mint_threshold(), dollar(1)); - assert_eq!(Homa::redeem_threshold(), dollar(10)); + assert_eq!(Homa::mint_threshold(), 1_000_000); + assert_eq!(Homa::redeem_threshold(), 10_000_000); assert_eq!(Homa::commission_rate(), Rate::saturating_from_rational(5, 100)); assert_eq!(Homa::fast_match_fee_rate(), Rate::saturating_from_rational(1, 100)); }); } #[test] -fn get_staking_currency_soft_cap_work() { +fn get_staking_currency_soft_cap_works() { ExtBuilder::default().build().execute_with(|| { assert_eq!(Homa::get_staking_currency_soft_cap(), 0); SoftBondedCapPerSubAccount::::put(1_000_000); @@ -164,7 +164,7 @@ fn get_staking_currency_soft_cap_work() { } #[test] -fn get_total_bonded_soft_cap_work() { +fn get_total_bonded_soft_cap_works() { ExtBuilder::default().build().execute_with(|| { StakingLedgers::::insert( 0, @@ -192,7 +192,7 @@ fn get_total_bonded_soft_cap_work() { } #[test] -fn get_total_staking_currency_work() { +fn get_total_staking_currency_works() { ExtBuilder::default().build().execute_with(|| { StakingLedgers::::insert( 0, @@ -214,7 +214,7 @@ fn get_total_staking_currency_work() { } #[test] -fn current_exchange_rate_work() { +fn current_exchange_rate_works() { ExtBuilder::default().build().execute_with(|| { assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_000_000)); @@ -250,7 +250,7 @@ fn current_exchange_rate_work() { } #[test] -fn distribution_helpers_work() { +fn distribution_helpers_works() { ExtBuilder::default().build().execute_with(|| { let bonded_list = vec![(0, 1_000_000), (1, 2_000_000), (2, 3_000_000), (3, 100_000)]; @@ -308,3 +308,962 @@ fn distribution_helpers_work() { ); }); } + +#[test] +fn mint_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, STAKING_CURRENCY_ID, 1_000_000), + (BOB, STAKING_CURRENCY_ID, 1_000_000), + ]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + Some(1_000_000), + None, + Some(100_000), + None, + None, + None, + )); + + assert_noop!( + Homa::mint(Origin::signed(ALICE), 99_999), + Error::::BelowMintThreshold + ); + assert_noop!( + Homa::mint(Origin::signed(ALICE), 3_000_001), + Error::::ExceededStakingCurrencySoftCap + ); + assert_noop!( + Homa::mint(Origin::signed(ALICE), 3_000_000), + orml_tokens::Error::::BalanceTooLow + ); + + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 0); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::get_total_staking_currency(), 0); + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 1_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + assert_ok!(Homa::mint(Origin::signed(ALICE), 500_000)); + System::assert_last_event(Event::Homa(crate::Event::Minted(ALICE, 500_000, 5_000_000, 0))); + + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 5_000_000); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::to_bond_pool(), 500_000); + assert_eq!(Homa::get_total_staking_currency(), 500_000); + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 5_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 500_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 500_000 + ); + + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + None, + Some(Rate::saturating_from_rational(10, 100)), + None, + None, + None, + None, + )); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 1_000_000); + + assert_ok!(Homa::mint(Origin::signed(BOB), 100_000)); + System::assert_last_event(Event::Homa(crate::Event::Minted(BOB, 100_000, 909_090, 90910))); + + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 5_909_090); + assert_eq!(Homa::total_void_liquid(), 90910); + assert_eq!(Homa::to_bond_pool(), 600_000); + assert_eq!(Homa::get_total_staking_currency(), 600_000); + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 909_090); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 900_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 600_000 + ); + }); +} + +#[test] +fn request_redeem_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, LIQUID_CURRENCY_ID, 10_000_000), + (BOB, LIQUID_CURRENCY_ID, 10_000_000), + ]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + None, + None, + None, + Some(1_000_000), + None, + None, + )); + + assert_noop!( + Homa::request_redeem(Origin::signed(ALICE), 999_999, false), + Error::::BelowRedeemThreshold + ); + + assert_eq!(Homa::redeem_requests(&ALICE), None); + assert_eq!(Homa::redeem_requests(&BOB), None); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 10_000_000); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 10_000_000); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); + + assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 1_000_000, false)); + System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(ALICE, 1_000_000, false))); + assert_eq!(Homa::redeem_requests(&ALICE), Some((1_000_000, false))); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 9_000_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 1_000_000 + ); + + assert_ok!(Homa::request_redeem(Origin::signed(BOB), 10_000_000, true)); + System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(BOB, 10_000_000, true))); + assert_eq!(Homa::redeem_requests(&BOB), Some((10_000_000, true))); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 0); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 11_000_000 + ); + + // Alice overwrite the redeem_request + assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 2_000_000, true)); + System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(ALICE, 2_000_000, true))); + assert_eq!(Homa::redeem_requests(&ALICE), Some((2_000_000, true))); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 8_000_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 12_000_000 + ); + + // Bob cancel the redeem_request + assert_ok!(Homa::request_redeem(Origin::signed(BOB), 0, false)); + System::assert_last_event(Event::Homa(crate::Event::RedeemRequestCancelled(BOB, 10_000_000))); + assert_eq!(Homa::redeem_requests(&BOB), None); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 10_000_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 2_000_000 + ); + }); +} + +#[test] +fn claim_redemption_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, LIQUID_CURRENCY_ID, 10_000_000), + (BOB, LIQUID_CURRENCY_ID, 10_000_000), + ]) + .build() + .execute_with(|| { + assert_eq!(Homa::relay_chain_current_era(), 0); + Unbondings::::insert(&ALICE, 1, 1_000_000); + Unbondings::::insert(&ALICE, 2, 2_000_000); + Unbondings::::insert(&ALICE, 3, 3_000_000); + assert_eq!(Homa::unbondings(&ALICE, 1), 1_000_000); + assert_eq!(Homa::unbondings(&ALICE, 2), 2_000_000); + assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + // no available expired redemption, nothing happend. + assert_ok!(Homa::claim_redemption(Origin::signed(BOB), ALICE)); + assert_eq!(Homa::unbondings(&ALICE, 1), 1_000_000); + assert_eq!(Homa::unbondings(&ALICE, 2), 2_000_000); + assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + // there is available expired redemption, but UnclaimedRedemption is not enought. + RelayChainCurrentEra::::put(2); + assert_noop!( + Homa::claim_redemption(Origin::signed(BOB), ALICE), + Error::::InsufficientUnclaimedRedemption + ); + + assert_ok!(Currencies::deposit(STAKING_CURRENCY_ID, &Homa::account_id(), 3_000_000)); + UnclaimedRedemption::::put(3_000_000); + assert_eq!(Homa::unclaimed_redemption(), 3_000_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 3_000_000 + ); + + assert_ok!(Homa::claim_redemption(Origin::signed(BOB), ALICE)); + assert_eq!(Homa::unbondings(&ALICE, 1), 0); + assert_eq!(Homa::unbondings(&ALICE, 2), 0); + assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 3_000_000); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + }); +} + +#[test] +fn do_fast_match_redeem_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, LIQUID_CURRENCY_ID, 20_000_000), + (BOB, LIQUID_CURRENCY_ID, 20_000_000), + (CHARLIE, STAKING_CURRENCY_ID, 1_000_000), + ]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_ledgers( + Origin::signed(HomaAdmin::get()), + vec![(0, Some(4_000_000), None)] + )); + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + Some(5_000_000), + None, + None, + Some(1_000_000), + None, + Some(Rate::saturating_from_rational(1, 10)), + )); + assert_ok!(Homa::mint(Origin::signed(CHARLIE), 1_000_000)); + assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 5_000_000, true)); + assert_ok!(Homa::request_redeem(Origin::signed(BOB), 6_500_000, true)); + assert_ok!(Homa::request_redeem(Origin::signed(CHARLIE), 5_000_000, false)); + assert_eq!(Homa::redeem_requests(&ALICE), Some((5_000_000, true))); + assert_eq!(Homa::redeem_requests(&BOB), Some((6_500_000, true))); + assert_eq!(Homa::redeem_requests(&CHARLIE), Some((5_000_000, false))); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &CHARLIE), 0); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 16_500_000 + ); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 1_000_000 + ); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 50_000_000); + assert_eq!(Homa::to_bond_pool(), 1_000_000); + assert_eq!(Homa::get_total_staking_currency(), 5_000_000); + assert_eq!( + Homa::current_exchange_rate(), + ExchangeRate::saturating_from_rational(5_000_000, 50_000_000) + ); + + // Charlie's redeem request is not allowed to be fast matched. + assert_noop!( + Homa::do_fast_match_redeem(&CHARLIE), + Error::::FastMatchIsNotAllowed + ); + + // Alice's redeem request is able to be fast matched fully. + assert_ok!(Homa::do_fast_match_redeem(&ALICE)); + System::assert_last_event(Event::Homa(crate::Event::RedeemedByFastMatch( + ALICE, 5_000_000, 500_000, 450_000, + ))); + assert_eq!(Homa::redeem_requests(&ALICE), None); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 450_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 11_500_000 + ); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 550_000 + ); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 45_000_000); + assert_eq!(Homa::to_bond_pool(), 550_000); + assert_eq!(Homa::get_total_staking_currency(), 4_550_000); + assert_eq!( + Homa::current_exchange_rate(), + ExchangeRate::saturating_from_rational(4_550_000, 45_000_000) + ); + + // Bob's redeem request is able to be fast matched partially, + // because must remain `RedeemThreshold` even if `ToBondPool` is enought. + assert_ok!(Homa::do_fast_match_redeem(&BOB)); + System::assert_last_event(Event::Homa(crate::Event::RedeemedByFastMatch( + BOB, 5_500_000, 550_000, 500_499, + ))); + assert_eq!(Homa::redeem_requests(&BOB), Some((1_000_000, true))); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 500_499); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 6_000_000 + ); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 49_501 + ); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 39_500_000); + assert_eq!(Homa::to_bond_pool(), 49_501); + assert_eq!(Homa::get_total_staking_currency(), 4_049_501); + assert_eq!( + Homa::current_exchange_rate(), + ExchangeRate::saturating_from_rational(4_049_501, 39_500_000) + ); + }); +} + +#[test] +fn draw_staking_reward_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, LIQUID_CURRENCY_ID, 40_000_000), + (CHARLIE, STAKING_CURRENCY_ID, 1_000_000), + ]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_ledgers( + Origin::signed(HomaAdmin::get()), + vec![(0, Some(4_000_000), None)] + )); + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + Some(5_000_000), + None, + None, + None, + Some(Rate::saturating_from_rational(10, 100)), + None, + )); + assert_ok!(Homa::mint(Origin::signed(CHARLIE), 1_000_000)); + assert_eq!(Homa::get_total_bonded(), 4_000_000); + assert_eq!(Homa::get_total_staking_currency(), 5_000_000); + assert_eq!(Homa::to_bond_pool(), 1_000_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 50_000_000); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), 0); + + assert_ok!(Homa::draw_staking_reward(2, 1)); + assert_eq!(Homa::get_total_bonded(), 4_000_000); + assert_eq!(Homa::get_total_staking_currency(), 5_000_000); + assert_eq!(Homa::to_bond_pool(), 1_000_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 50_000_000); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), 0); + + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + None, + Some(Rate::saturating_from_rational(20, 100)), + None, + None, + None, + None, + )); + assert_ok!(Homa::draw_staking_reward(3, 2)); + assert_eq!(Homa::get_total_bonded(), 4_000_000); + assert_eq!(Homa::get_total_staking_currency(), 5_000_000); + assert_eq!(Homa::to_bond_pool(), 1_000_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 50_699_300); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), + 699_300 + ); + }); +} + +#[test] +fn process_scheduled_unbond_works() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Homa::update_ledgers( + Origin::signed(HomaAdmin::get()), + vec![ + ( + 0, + None, + Some(vec![ + UnlockChunk { + value: 1_000_000, + era: 11 + }, + UnlockChunk { + value: 2_000_000, + era: 14 + }, + ]) + ), + ( + 1, + None, + Some(vec![ + UnlockChunk { + value: 100_000, + era: 12 + }, + UnlockChunk { + value: 200_000, + era: 13 + }, + ]) + ), + ] + )); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 0, + unlocking: vec![ + UnlockChunk { + value: 1_000_000, + era: 11 + }, + UnlockChunk { + value: 2_000_000, + era: 14 + }, + ] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 0, + unlocking: vec![ + UnlockChunk { + value: 100_000, + era: 12 + }, + UnlockChunk { + value: 200_000, + era: 13 + }, + ] + }) + ); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + assert_ok!(Homa::process_scheduled_unbond(13)); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 0, + unlocking: vec![UnlockChunk { + value: 2_000_000, + era: 14 + },] + }) + ); + assert_eq!(Homa::staking_ledgers(1), None); + assert_eq!(Homa::unclaimed_redemption(), 1_300_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 1_300_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 1_300_000 + ); + }); +} + +#[test] +fn process_to_bond_pool_works() { + ExtBuilder::default() + .balances(vec![(ALICE, STAKING_CURRENCY_ID, 20_000_000)]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + Some(3_000_000), + None, + None, + None, + None, + None, + )); + assert_ok!(Homa::update_ledgers( + Origin::signed(HomaAdmin::get()), + vec![(0, Some(1_000_000), None)] + )); + assert_ok!(Homa::mint(Origin::signed(ALICE), 900_000)); + assert_eq!(MockHomaSubAccountXcm::get_xcm_transfer_fee(), 1_000_000); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::staking_ledgers(1), None); + assert_eq!(Homa::staking_ledgers(2), None); + assert_eq!(Homa::to_bond_pool(), 900_000); + assert_eq!(Homa::get_total_bonded(), 1_000_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 20_000_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 900_000 + ); + + // ToBondPool is unable to afford xcm_transfer_fee + assert_ok!(Homa::process_to_bond_pool(1)); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::staking_ledgers(1), None); + assert_eq!(Homa::staking_ledgers(2), None); + assert_eq!(Homa::to_bond_pool(), 900_000); + assert_eq!(Homa::get_total_bonded(), 1_000_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 20_000_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 900_000 + ); + + // ToBondPool is able to afford xcm_transfer_fee, but no bonded added + assert_ok!(Homa::mint(Origin::signed(ALICE), 100_000)); + assert_eq!(Homa::to_bond_pool(), 1_000_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 20_000_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 1_000_000 + ); + assert_ok!(Homa::process_to_bond_pool(2)); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::staking_ledgers(1), None); + assert_eq!(Homa::staking_ledgers(2), None); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::get_total_bonded(), 1_000_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 19_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + // ToBondPool is able to afford xcm_transfer_fee, and bonded added + assert_ok!(Homa::mint(Origin::signed(ALICE), 6_000_000)); + assert_eq!(Homa::to_bond_pool(), 6_000_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 19_000_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 6_000_000 + ); + assert_ok!(Homa::process_to_bond_pool(3)); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 3_000_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(2), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::get_total_bonded(), 5_000_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 13_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + // ToBondPool is able to afford xcm_transfer_fee, and below the mint_threshold, no bonded added. + assert_ok!(Homa::mint(Origin::signed(ALICE), 2_000_000)); + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + None, + None, + Some(3_000_000), + None, + None, + None, + )); + assert_eq!(Homa::to_bond_pool(), 2_000_000); + assert_eq!(Homa::get_total_bonded(), 5_000_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 13_000_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 2_000_000 + ); + assert_ok!(Homa::process_to_bond_pool(4)); + assert_eq!(Homa::to_bond_pool(), 2_000_000); + assert_eq!(Homa::get_total_bonded(), 5_000_000); + assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 13_000_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 2_000_000 + ); + }); +} + +#[test] +fn process_redeem_requests_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, LIQUID_CURRENCY_ID, 20_000_000), + (BOB, LIQUID_CURRENCY_ID, 20_000_000), + (CHARLIE, LIQUID_CURRENCY_ID, 10_000_000), + (DAVE, LIQUID_CURRENCY_ID, 10_000_000), + ]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_ledgers( + Origin::signed(HomaAdmin::get()), + vec![(0, Some(2_000_000), None), (1, Some(3_000_000), None),] + )); + ToBondPool::::put(1_000_000); + assert_eq!(Homa::relay_chain_current_era(), 0); + + assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 20_000_000, false)); + assert_eq!(Homa::redeem_requests(&ALICE), Some((20_000_000, false))); + assert_eq!(Homa::unbondings(&ALICE, 1 + BondingDuration::get()), 0); + assert_eq!(Homa::get_total_bonded(), 5_000_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 60_000_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 20_000_000 + ); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 2_000_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 3_000_000, + unlocking: vec![] + }) + ); + + // total_bonded is enought to process all redeem requests + assert_ok!(Homa::process_redeem_requests(1)); + System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( + ALICE, 1, 20_000_000, 2_000_000, + ))); + assert_eq!(Homa::redeem_requests(&ALICE), None); + assert_eq!(Homa::unbondings(&ALICE, 1 + BondingDuration::get()), 2_000_000); + assert_eq!(Homa::get_total_bonded(), 3_000_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 40_000_000); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 2_000_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![UnlockChunk { + value: 2_000_000, + era: 1 + BondingDuration::get() + }] + }) + ); + + assert_ok!(Homa::request_redeem(Origin::signed(BOB), 20_000_000, false)); + assert_ok!(Homa::request_redeem(Origin::signed(CHARLIE), 10_000_000, false)); + assert_ok!(Homa::request_redeem(Origin::signed(DAVE), 10_000_000, false)); + assert_eq!(Homa::redeem_requests(&BOB), Some((20_000_000, false))); + assert_eq!(Homa::redeem_requests(&CHARLIE), Some((10_000_000, false))); + assert_eq!(Homa::redeem_requests(&DAVE), Some((10_000_000, false))); + assert_eq!(Homa::unbondings(&BOB, 2 + BondingDuration::get()), 0); + assert_eq!(Homa::unbondings(&CHARLIE, 2 + BondingDuration::get()), 0); + assert_eq!(Homa::unbondings(&DAVE, 2 + BondingDuration::get()), 0); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 40_000_000 + ); + + // total_bonded is not enought to process all redeem requests + assert_ok!(Homa::process_redeem_requests(2)); + System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( + BOB, 2, 20_000_000, 2_000_000, + ))); + System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( + CHARLIE, 2, 10_000_000, 1_000_000, + ))); + assert_eq!(Homa::redeem_requests(&BOB), None); + assert_eq!(Homa::redeem_requests(&CHARLIE), None); + assert_eq!(Homa::redeem_requests(&DAVE), Some((10_000_000, false))); + assert_eq!(Homa::unbondings(&BOB, 2 + BondingDuration::get()), 2_000_000); + assert_eq!(Homa::unbondings(&CHARLIE, 2 + BondingDuration::get()), 1_000_000); + assert_eq!(Homa::unbondings(&DAVE, 2 + BondingDuration::get()), 0); + assert_eq!(Homa::get_total_bonded(), 0); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 10_000_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 10_000_000 + ); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 0, + unlocking: vec![UnlockChunk { + value: 2_000_000, + era: 2 + BondingDuration::get() + }] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 0, + unlocking: vec![ + UnlockChunk { + value: 2_000_000, + era: 1 + BondingDuration::get() + }, + UnlockChunk { + value: 1_000_000, + era: 2 + BondingDuration::get() + } + ] + }) + ); + }); +} + +#[test] +fn bump_new_era_works() { + ExtBuilder::default() + .balances(vec![(ALICE, STAKING_CURRENCY_ID, 100_000_000)]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + Some(20_000_000), + Some(Rate::saturating_from_rational(1, 100)), + Some(2_000_000), + None, + Some(Rate::saturating_from_rational(20, 100)), + None, + )); + + // initial states at era #0 + assert_eq!(Homa::relay_chain_current_era(), 0); + assert_eq!(Homa::staking_ledgers(0), None); + assert_eq!(Homa::staking_ledgers(1), None); + assert_eq!(Homa::staking_ledgers(2), None); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::get_total_staking_currency(), 0); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), 0); + + assert_ok!(Homa::mint(Origin::signed(ALICE), 30_000_000)); + assert_eq!(Homa::to_bond_pool(), 30_000_000); + assert_eq!(Homa::total_void_liquid(), 2_970_298); + assert_eq!(Homa::get_total_staking_currency(), 30_000_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 297_029_702); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 30_000_000 + ); + + // bump era failed when caller has no permission. + assert_noop!(Homa::bump_current_era(Origin::signed(ALICE), 1), BadOrigin); + + // bump era failed when new era is outdated. + assert_noop!( + Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 0), + Error::::OutdatedEraIndex + ); + + // bump era to #1, + // will process to_bond_pool. + assert_ok!(Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 1)); + System::assert_last_event(Event::Homa(crate::Event::CurrentEraBumped(1))); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 20_000_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 8_000_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::staking_ledgers(2), None); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::get_total_staking_currency(), 28_000_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 297_029_702); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), 0); + + // mock update the staking reward to StakingLedgers + assert_ok!(Homa::update_ledgers( + Origin::signed(HomaAdmin::get()), + vec![(0, Some(20_300_000), None), (1, Some(8_080_000), None),] + )); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 20_300_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 8_080_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::get_total_staking_currency(), 28_380_000); + + // bump era to #2, + // commission drawn from the staking reward is not zero + assert_ok!(Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 2)); + System::assert_last_event(Event::Homa(crate::Event::CurrentEraBumped(2))); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 20_300_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 8_080_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::staking_ledgers(2), None); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::get_total_staking_currency(), 28_380_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 297_619_046); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), + 589_344 + ); + + // assuming now staking has no rewards any more. + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + None, + Some(Rate::zero()), + None, + None, + None, + None, + )); + + // and there's redeem request + assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 280_000_000, false)); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 280_000_000 + ); + + // bump era to #3, + // will process redeem requests + assert_ok!(Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 3)); + System::assert_last_event(Event::Homa(crate::Event::CurrentEraBumped(3))); + System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( + ALICE, + 3, + 280_000_000, + 26_699_904, + ))); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 0, + unlocking: vec![UnlockChunk { + value: 20_300_000, + era: 3 + BondingDuration::get() + }] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 1_680_096, + unlocking: vec![UnlockChunk { + value: 6_399_904, + era: 3 + BondingDuration::get() + }] + }) + ); + assert_eq!(Homa::staking_ledgers(2), None); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::get_total_staking_currency(), 1_680_096); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 17_619_046); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), + 589_344 + ); + + // bump era to #31, + // will process scheduled unbonded + assert_ok!(Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 31)); + System::assert_last_event(Event::Homa(crate::Event::CurrentEraBumped(31))); + assert_eq!(Homa::staking_ledgers(0), None); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 1_680_096, + unlocking: vec![] + }) + ); + assert_eq!(Homa::staking_ledgers(2), None); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::unclaimed_redemption(), 26_699_904); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::get_total_staking_currency(), 1_680_096); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 17_619_046); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 26_699_904 + ); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), + 589_344 + ); + }); +} From d0c069d4b81ec62e19f1c0f5521f2ca3460e754a Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Tue, 7 Dec 2021 18:39:04 +0800 Subject: [PATCH 09/27] some updates --- Cargo.lock | 1 + modules/homa/Cargo.toml | 2 + modules/homa/src/lib.rs | 343 ++++++++------ modules/homa/src/tests.rs | 969 ++++++++++++++++++++------------------ 4 files changed, 735 insertions(+), 580 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c05ecc144c..23b0e41396 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5582,6 +5582,7 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", + "sp-arithmetic", "sp-core", "sp-io", "sp-runtime", diff --git a/modules/homa/Cargo.toml b/modules/homa/Cargo.toml index 444899dc5f..bc543c2550 100644 --- a/modules/homa/Cargo.toml +++ b/modules/homa/Cargo.toml @@ -10,6 +10,7 @@ scale-info = { version = "1.0", default-features = false, features = ["derive"] frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13", default-features = false, optional = true} frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13", default-features = false } @@ -31,6 +32,7 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", + "sp-arithmetic/std", "sp-runtime/std", "sp-core/std", "sp-std/std", diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 9ee66cddce..5781fc1e95 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -27,8 +27,9 @@ use module_support::{ExchangeRate, ExchangeRateProvider, HomaSubAccountXcm, Rate use orml_traits::MultiCurrency; use primitives::{Balance, CurrencyId, EraIndex}; use scale_info::TypeInfo; +use sp_arithmetic::traits::CheckedRem; use sp_runtime::{ - traits::{AccountIdConversion, Bounded, CheckedDiv, One, Saturating, UniqueSaturatedInto, Zero}, + traits::{AccountIdConversion, Bounded, CheckedAdd, CheckedDiv, One, Saturating, UniqueSaturatedInto, Zero}, ArithmeticError, FixedPointNumber, }; use sp_std::{cmp::Ordering, convert::From, prelude::*, vec, vec::Vec}; @@ -170,12 +171,14 @@ pub mod module { WithdrawRedemption(T::AccountId, Balance), /// The current era has been bumped. \[new_era_index\] CurrentEraBumped(EraIndex), - /// The bonded amount of subaccount's ledger has been updated. \[sub_account_index, + /// The current era has been reset. \[new_era_index\] + CurrentEraReset(EraIndex), + /// The bonded amount of subaccount's ledger has been reset. \[sub_account_index, /// new_bonded_amount\] - LedgerBondedUpdated(u16, Balance), - /// The unlocking of subaccount's ledger has been updated. \[sub_account_index, + LedgerBondedReset(u16, Balance), + /// The unlocking of subaccount's ledger has been reset. \[sub_account_index, /// new_unlocking\] - LedgerUnlockingUpdated(u16, Vec), + LedgerUnlockingReset(u16, Vec), /// The soft bonded cap of per sub account has been updated. \[cap_amount\] SoftBondedCapPerSubAccountUpdated(Balance), /// The estimated reward rate per era of relaychain staking has been updated. @@ -189,6 +192,10 @@ pub mod module { CommissionRateUpdated(Rate), /// The fast match fee rate has been updated. \[commission_rate\] FastMatchFeeRateUpdated(Rate), + /// The prefix to bump era has been updated. \[prefix_blocks\] + BumpEraPrefixUpdated(T::BlockNumber), + /// The frequency to bump era has been updated. \[frequency_blocks\] + BumpEraFrequencyUpdated(T::BlockNumber), } /// The current era of relaychain @@ -296,11 +303,32 @@ pub mod module { #[pallet::getter(fn fast_match_fee_rate)] pub type FastMatchFeeRate = StorageValue<_, Rate, ValueQuery>; + /// The prefix of block numbers to bump local current era. + /// + /// BumpEraPrefix: value: BlockNumber + #[pallet::storage] + #[pallet::getter(fn bump_era_prefix)] + pub type BumpEraPrefix = StorageValue<_, T::BlockNumber, ValueQuery>; + + /// The internal of blocks numbers to bump local current era. + #[pallet::storage] + #[pallet::getter(fn bump_era_frequency)] + pub type BumpEraFrequency = StorageValue<_, T::BlockNumber, ValueQuery>; + #[pallet::pallet] pub struct Pallet(_); #[pallet::hooks] - impl Hooks for Pallet {} + impl Hooks for Pallet { + fn on_initialize(n: T::BlockNumber) -> Weight { + let current_weight = 0; + if Self::should_bump_local_current_era(n) { + let _ = Self::bump_current_era(); + } + + current_weight + } + } #[pallet::call] impl Pallet { @@ -467,46 +495,95 @@ pub mod module { Ok(()) } - /// Bump the current era to keep consistent with relaychain. + /// Sets the params of Homa. /// Requires `GovernanceOrigin` /// /// Parameters: - /// - `new_era`: the latest era index of relaychain. + /// - `soft_bonded_cap_per_sub_account`: soft cap of staking amount for a single nominator + /// on relaychain to obtain the best staking rewards. + /// - `estimated_reward_rate_per_era`: the esstaking yield of each era on the current relay + /// chain. + /// - `mint_threshold`: the staking currency amount of threshold when mint. + /// - `redeem_threshold`: the liquid currency amount of threshold when request redeem. + /// - `commission_rate`: the rate to draw from estimated staking rewards as commission to + /// HomaTreasury + /// - `fast_match_fee_rate`: the fixed fee rate when redeem request is been fast matched. #[pallet::weight(10_000)] #[transactional] - pub fn bump_current_era(origin: OriginFor, new_era: EraIndex) -> DispatchResult { + pub fn update_homa_params( + origin: OriginFor, + soft_bonded_cap_per_sub_account: Option, + estimated_reward_rate_per_era: Option, + mint_threshold: Option, + redeem_threshold: Option, + commission_rate: Option, + fast_match_fee_rate: Option, + ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; - RelayChainCurrentEra::::try_mutate(|current_era| -> DispatchResult { - ensure!(new_era > *current_era, Error::::OutdatedEraIndex); + if let Some(change) = soft_bonded_cap_per_sub_account { + SoftBondedCapPerSubAccount::::put(change); + Self::deposit_event(Event::::SoftBondedCapPerSubAccountUpdated(change)); + } + if let Some(change) = estimated_reward_rate_per_era { + EstimatedRewardRatePerEra::::put(change); + Self::deposit_event(Event::::EstimatedRewardRatePerEraUpdated(change)); + } + if let Some(change) = mint_threshold { + MintThreshold::::put(change); + Self::deposit_event(Event::::MintThresholdUpdated(change)); + } + if let Some(change) = redeem_threshold { + RedeemThreshold::::put(change); + Self::deposit_event(Event::::RedeemThresholdUpdated(change)); + } + if let Some(change) = commission_rate { + CommissionRate::::put(change); + Self::deposit_event(Event::::CommissionRateUpdated(change)); + } + if let Some(change) = fast_match_fee_rate { + FastMatchFeeRate::::put(change); + Self::deposit_event(Event::::FastMatchFeeRateUpdated(change)); + } - // reset void liquid to zero firstly, to guarantee - TotalVoidLiquid::::put(0); + Ok(()) + } - // TODO: consider execute rebalance on on_idle, before the processing is completed, - // the mint and request_redeem functions should be unavailable. - // Rebalance: - Self::draw_staking_reward(new_era, *current_era)?; - Self::process_scheduled_unbond(new_era)?; - Self::process_to_bond_pool(new_era)?; - Self::process_redeem_requests(new_era)?; + /// Sets the params that control when to bump local current era. + /// Requires `GovernanceOrigin` + /// + /// Parameters: + /// - `prefix`: the prefix of block number on parachain. + /// - `frequency`: the frequency of block number on parachain. + #[pallet::weight(10_000)] + #[transactional] + pub fn update_bump_era_params( + origin: OriginFor, + prefix: Option, + frequency: Option, + ) -> DispatchResult { + T::GovernanceOrigin::ensure_origin(origin)?; - // bump current era to latest. - *current_era = new_era; + if let Some(change) = prefix { + BumpEraPrefix::::put(change); + Self::deposit_event(Event::::BumpEraPrefixUpdated(change)); + } + if let Some(change) = frequency { + BumpEraFrequency::::put(change); + Self::deposit_event(Event::::BumpEraFrequencyUpdated(change)); + } - Self::deposit_event(Event::::CurrentEraBumped(new_era)); - Ok(()) - }) + Ok(()) } - /// Update the bonded and unbonding to local subaccounts ledger according to the ledger on + /// Reset the bonded and unbonding to local subaccounts ledger according to the ledger on /// relaychain. Requires `GovernanceOrigin` /// /// Parameters: - /// - `new_era`: the latest era index of relaychain. + /// - `updates`: update list of subaccount. #[pallet::weight(10_000)] #[transactional] - pub fn update_ledgers( + pub fn reset_ledgers( origin: OriginFor, updates: Vec<(u16, Option, Option>)>, ) -> DispatchResult { @@ -514,11 +591,17 @@ pub mod module { for (sub_account_index, bonded_change, unlocking_change) in updates { Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { - if let Some(bonded) = bonded_change { - ledger.bonded = bonded; + if let Some(change) = bonded_change { + if ledger.bonded != change { + ledger.bonded = change; + Self::deposit_event(Event::::LedgerBondedReset(sub_account_index, change)); + } } - if let Some(unlocking) = unlocking_change { - ledger.unlocking = unlocking; + if let Some(change) = unlocking_change { + if ledger.unlocking != change { + ledger.unlocking = change.clone(); + Self::deposit_event(Event::::LedgerUnlockingReset(sub_account_index, change)); + } } Ok(()) })?; @@ -527,56 +610,25 @@ pub mod module { Ok(()) } - /// Sets the params of Homa. + /// Reset the RelayChainCurrentEra. + /// If there is a deviation of more than 1 EraIndex between current era of relaychain and + /// current era on local, should reset era to current era of relaychain as soon as possible. + /// At the same time, check whether the unlocking of ledgers should be updated. /// Requires `GovernanceOrigin` /// /// Parameters: - /// - `soft_bonded_cap_per_sub_account`: soft cap of staking amount for a single nominator - /// on relaychain to obtain the best staking rewards. - /// - `estimated_reward_rate_per_era`: the esstaking yield of each era on the current relay - /// chain. - /// - `mint_threshold`: the staking currency amount of threshold when mint. - /// - `redeem_threshold`: the liquid currency amount of threshold when request redeem. - /// - `commission_rate`: the rate to draw from estimated staking rewards as commission to - /// HomaTreasury - /// - `fast_match_fee_rate`: the fixed fee rate when redeem request is been fast matched. + /// - `era_index`: the latest era index of relaychain. #[pallet::weight(10_000)] #[transactional] - pub fn update_homa_params( - origin: OriginFor, - soft_bonded_cap_per_sub_account: Option, - estimated_reward_rate_per_era: Option, - mint_threshold: Option, - redeem_threshold: Option, - commission_rate: Option, - fast_match_fee_rate: Option, - ) -> DispatchResult { + pub fn reset_current_era(origin: OriginFor, era_index: EraIndex) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; - if let Some(cap) = soft_bonded_cap_per_sub_account { - SoftBondedCapPerSubAccount::::put(cap); - Self::deposit_event(Event::::SoftBondedCapPerSubAccountUpdated(cap)); - } - if let Some(rate) = estimated_reward_rate_per_era { - EstimatedRewardRatePerEra::::put(rate); - Self::deposit_event(Event::::EstimatedRewardRatePerEraUpdated(rate)); - } - if let Some(threshold) = mint_threshold { - MintThreshold::::put(threshold); - Self::deposit_event(Event::::MintThresholdUpdated(threshold)); - } - if let Some(threshold) = redeem_threshold { - RedeemThreshold::::put(threshold); - Self::deposit_event(Event::::RedeemThresholdUpdated(threshold)); - } - if let Some(rate) = commission_rate { - CommissionRate::::put(rate); - Self::deposit_event(Event::::CommissionRateUpdated(rate)); - } - if let Some(rate) = fast_match_fee_rate { - FastMatchFeeRate::::put(rate); - Self::deposit_event(Event::::FastMatchFeeRateUpdated(rate)); - } + RelayChainCurrentEra::::mutate(|current_era| { + if *current_era != era_index { + *current_era = era_index; + Self::deposit_event(Event::::CurrentEraReset(era_index)); + } + }); Ok(()) } @@ -588,31 +640,18 @@ pub mod module { T::PalletId::get().into_account() } - fn do_update_ledger( + pub fn do_update_ledger( sub_account_index: u16, f: impl FnOnce(&mut StakingLedger) -> sp_std::result::Result, ) -> sp_std::result::Result { StakingLedgers::::try_mutate_exists(sub_account_index, |maybe_ledger| { let mut ledger = maybe_ledger.take().unwrap_or_default(); - let old_ledger = ledger.clone(); - f(&mut ledger).map(move |result| { - if ledger.bonded != old_ledger.bonded { - Self::deposit_event(Event::::LedgerBondedUpdated(sub_account_index, ledger.bonded)); - } - if ledger.unlocking != old_ledger.unlocking { - Self::deposit_event(Event::::LedgerUnlockingUpdated( - sub_account_index, - ledger.unlocking.clone(), - )); - } - *maybe_ledger = if ledger == Default::default() { None } else { Some(ledger) }; - result }) }) @@ -735,45 +774,61 @@ pub mod module { }) } - /// Draw commission to TreasuryAccount from estimated staking rewards. Commission will be - /// given to TreasuryAccount by issuing liquid currency. Note: This will cause some losses - /// to the minters in previous_era, because they have been already deducted some liquid - /// currency amount when mint in previous_era. Until there is a better way to calculate, - /// this part of the loss can only be regarded as an implicit mint fee! + /// Accumulate staking rewards according to EstimatedRewardRatePerEra and era interal. + /// And draw commission from estimated staking rewards by issuing liquid currency to + /// TreasuryAccount. Note: This will cause some losses to the minters in previous_era, + /// because they have been already deducted some liquid currency amount when mint in + /// previous_era. Until there is a better way to calculate, this part of the loss can only + /// be regarded as an implicit mint fee! #[transactional] - pub fn draw_staking_reward(new_era: EraIndex, previous_era: EraIndex) -> DispatchResult { + pub fn process_staking_rewards(new_era: EraIndex, previous_era: EraIndex) -> DispatchResult { let era_interval = new_era.saturating_sub(previous_era); - let liquid_currency_id = T::LiquidCurrencyId::get(); - let bond_ratio = Ratio::checked_from_rational(Self::get_total_bonded(), Self::get_total_staking_currency()) - .unwrap_or_else(Ratio::zero); - let reward_rate = bond_ratio - .saturating_mul(Self::estimated_reward_rate_per_era()) + let reward_rate = Self::estimated_reward_rate_per_era() .saturating_add(Rate::one()) .saturating_pow(era_interval.unique_saturated_into()) .saturating_sub(Rate::one()); - let commission_rate = reward_rate.saturating_mul(Self::commission_rate()); - let inflation_rate = commission_rate - .checked_div(&Rate::one().saturating_add(reward_rate).saturating_sub(commission_rate)) - .expect("shouldn't be invalid!"); - let liquid_amount_as_commision = - inflation_rate.saturating_mul_int(T::Currency::total_issuance(liquid_currency_id)); - T::Currency::deposit( - liquid_currency_id, - &T::TreasuryAccount::get(), - liquid_amount_as_commision, - ) + + if !reward_rate.is_zero() { + let mut total_reward_staking: Balance = Zero::zero(); + + // iterate all subaccounts + for (sub_account_index, ledger) in StakingLedgers::::iter() { + let reward_staking = reward_rate.saturating_mul_int(ledger.bonded); + + if !reward_staking.is_zero() { + Self::do_update_ledger(sub_account_index, |before| -> DispatchResult { + before.bonded = before.bonded.saturating_add(reward_staking); + Ok(()) + })?; + + total_reward_staking = total_reward_staking.saturating_add(reward_staking); + } + } + + let commission_rate = Self::commission_rate(); + if !total_reward_staking.is_zero() && !commission_rate.is_zero() { + let liquid_currency_id = T::LiquidCurrencyId::get(); + let commission_staking_amount = commission_rate.saturating_mul_int(total_reward_staking); + let commission_ratio = + Ratio::checked_from_rational(commission_staking_amount, Self::get_total_bonded()) + .unwrap_or_else(Ratio::min_value); + let inflate_rate = commission_ratio + .checked_div(&Ratio::one().saturating_sub(commission_ratio)) + .unwrap_or_else(Ratio::max_value); + let inflate_liquid_amount = + inflate_rate.saturating_mul_int(T::Currency::total_issuance(liquid_currency_id)); + + T::Currency::deposit(liquid_currency_id, &T::TreasuryAccount::get(), inflate_liquid_amount)?; + } + } + + Ok(()) } /// Get back unbonded of all subaccounts on relaychain by XCM. /// The staking currency withdrew becomes available to be redeemed. #[transactional] pub fn process_scheduled_unbond(new_era: EraIndex) -> DispatchResult { - log::debug!( - target: "homa", - "process scheduled unbond on era: {:?}", - new_era - ); - let mut total_withdrawn_staking: Balance = Zero::zero(); // iterate all subaccounts @@ -806,13 +861,7 @@ pub mod module { /// Distribute PoolToBond to ActiveSubAccountsIndexList, then cross-transfer the /// distribution amount to the subaccounts on relaychain and bond it by XCM. #[transactional] - pub fn process_to_bond_pool(new_era: EraIndex) -> DispatchResult { - log::debug!( - target: "homa", - "process to bond pool on era: {:?}", - new_era - ); - + pub fn process_to_bond_pool() -> DispatchResult { let to_bond_pool = Self::to_bond_pool(); // if to_bond is gte than mint_threshold, try to bond_extra on relaychain @@ -855,12 +904,6 @@ pub mod module { /// Process redeem requests and subaccounts do unbond on relaychain by XCM message. #[transactional] pub fn process_redeem_requests(new_era: EraIndex) -> DispatchResult { - log::debug!( - target: "homa", - "process redeem requests on era: {:?}", - new_era - ); - let era_index_to_expire = new_era + T::BondingDuration::get(); let total_bonded = Self::get_total_bonded(); let mut total_redeem_amount: Balance = Zero::zero(); @@ -874,7 +917,9 @@ pub mod module { total_redeem_amount = total_redeem_amount.saturating_add(redeem_amount); remain_total_bonded = remain_total_bonded.saturating_sub(redemption_amount); RedeemRequests::::remove(&redeemer); - Unbondings::::insert(&redeemer, era_index_to_expire, redemption_amount); + Unbondings::::mutate(&redeemer, era_index_to_expire, |n| { + *n = n.saturating_add(redemption_amount) + }); Self::deposit_event(Event::::RedeemedByUnbond( redeemer, new_era, @@ -914,6 +959,42 @@ pub mod module { // burn total_redeem_amount. T::Currency::withdraw(T::LiquidCurrencyId::get(), &Self::account_id(), total_redeem_amount) } + + pub fn should_bump_local_current_era(block_number: T::BlockNumber) -> bool { + block_number + .checked_add(&Self::bump_era_prefix()) + .and_then(|n| n.checked_rem(&Self::bump_era_frequency())) + .map_or(false, |n| n.is_zero()) + } + + /// Bump current era. + /// The rebalance will send XCM messages to relaychain. Once the XCM message is sent, + /// the execution result cannot be obtained and cannot be rolled back. So the process + /// of rebalance is not atomic. + pub fn bump_current_era() -> DispatchResult { + let previous_era = Self::relay_chain_current_era(); + let new_era = previous_era + 1; + RelayChainCurrentEra::::put(new_era); + Self::deposit_event(Event::::CurrentEraBumped(new_era)); + + // Rebalance: + let res = || -> DispatchResult { + TotalVoidLiquid::::put(0); + Self::process_staking_rewards(new_era, previous_era)?; + Self::process_scheduled_unbond(new_era)?; + Self::process_to_bond_pool()?; + Self::process_redeem_requests(new_era)?; + Ok(()) + }(); + + log::debug!( + target: "homa", + "bump era to {:?}, rebalance result is {:?}", + new_era, res + ); + + res + } } impl ExchangeRateProvider for Pallet { diff --git a/modules/homa/src/tests.rs b/modules/homa/src/tests.rs index fd279a9e0c..a9ca4bda13 100644 --- a/modules/homa/src/tests.rs +++ b/modules/homa/src/tests.rs @@ -27,14 +27,294 @@ use orml_traits::MultiCurrency; use sp_runtime::{traits::BadOrigin, FixedPointNumber}; #[test] -fn update_ledgers_works() { +fn mint_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, STAKING_CURRENCY_ID, 1_000_000), + (BOB, STAKING_CURRENCY_ID, 1_000_000), + ]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + Some(1_000_000), + None, + Some(100_000), + None, + None, + None, + )); + + assert_noop!( + Homa::mint(Origin::signed(ALICE), 99_999), + Error::::BelowMintThreshold + ); + assert_noop!( + Homa::mint(Origin::signed(ALICE), 3_000_001), + Error::::ExceededStakingCurrencySoftCap + ); + assert_noop!( + Homa::mint(Origin::signed(ALICE), 3_000_000), + orml_tokens::Error::::BalanceTooLow + ); + + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 0); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::to_bond_pool(), 0); + assert_eq!(Homa::get_total_staking_currency(), 0); + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 1_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + assert_ok!(Homa::mint(Origin::signed(ALICE), 500_000)); + System::assert_last_event(Event::Homa(crate::Event::Minted(ALICE, 500_000, 5_000_000, 0))); + + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 5_000_000); + assert_eq!(Homa::total_void_liquid(), 0); + assert_eq!(Homa::to_bond_pool(), 500_000); + assert_eq!(Homa::get_total_staking_currency(), 500_000); + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 5_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 500_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 500_000 + ); + + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + None, + Some(Rate::saturating_from_rational(10, 100)), + None, + None, + None, + None, + )); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 1_000_000); + + assert_ok!(Homa::mint(Origin::signed(BOB), 100_000)); + System::assert_last_event(Event::Homa(crate::Event::Minted(BOB, 100_000, 909_090, 90910))); + + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 5_909_090); + assert_eq!(Homa::total_void_liquid(), 90910); + assert_eq!(Homa::to_bond_pool(), 600_000); + assert_eq!(Homa::get_total_staking_currency(), 600_000); + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 909_090); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 900_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 600_000 + ); + }); +} + +#[test] +fn request_redeem_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, LIQUID_CURRENCY_ID, 10_000_000), + (BOB, LIQUID_CURRENCY_ID, 10_000_000), + ]) + .build() + .execute_with(|| { + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + None, + None, + None, + Some(1_000_000), + None, + None, + )); + + assert_noop!( + Homa::request_redeem(Origin::signed(ALICE), 999_999, false), + Error::::BelowRedeemThreshold + ); + + assert_eq!(Homa::redeem_requests(&ALICE), None); + assert_eq!(Homa::redeem_requests(&BOB), None); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 10_000_000); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 10_000_000); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); + + assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 1_000_000, false)); + System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(ALICE, 1_000_000, false))); + assert_eq!(Homa::redeem_requests(&ALICE), Some((1_000_000, false))); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 9_000_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 1_000_000 + ); + + assert_ok!(Homa::request_redeem(Origin::signed(BOB), 10_000_000, true)); + System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(BOB, 10_000_000, true))); + assert_eq!(Homa::redeem_requests(&BOB), Some((10_000_000, true))); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 0); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 11_000_000 + ); + + // Alice overwrite the redeem_request + assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 2_000_000, true)); + System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(ALICE, 2_000_000, true))); + assert_eq!(Homa::redeem_requests(&ALICE), Some((2_000_000, true))); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 8_000_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 12_000_000 + ); + + // Bob cancel the redeem_request + assert_ok!(Homa::request_redeem(Origin::signed(BOB), 0, false)); + System::assert_last_event(Event::Homa(crate::Event::RedeemRequestCancelled(BOB, 10_000_000))); + assert_eq!(Homa::redeem_requests(&BOB), None); + assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 10_000_000); + assert_eq!( + Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), + 2_000_000 + ); + }); +} + +#[test] +fn claim_redemption_works() { + ExtBuilder::default() + .balances(vec![ + (ALICE, LIQUID_CURRENCY_ID, 10_000_000), + (BOB, LIQUID_CURRENCY_ID, 10_000_000), + ]) + .build() + .execute_with(|| { + assert_eq!(Homa::relay_chain_current_era(), 0); + Unbondings::::insert(&ALICE, 1, 1_000_000); + Unbondings::::insert(&ALICE, 2, 2_000_000); + Unbondings::::insert(&ALICE, 3, 3_000_000); + assert_eq!(Homa::unbondings(&ALICE, 1), 1_000_000); + assert_eq!(Homa::unbondings(&ALICE, 2), 2_000_000); + assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + // no available expired redemption, nothing happend. + assert_ok!(Homa::claim_redemption(Origin::signed(BOB), ALICE)); + assert_eq!(Homa::unbondings(&ALICE, 1), 1_000_000); + assert_eq!(Homa::unbondings(&ALICE, 2), 2_000_000); + assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + + // there is available expired redemption, but UnclaimedRedemption is not enought. + RelayChainCurrentEra::::put(2); + assert_noop!( + Homa::claim_redemption(Origin::signed(BOB), ALICE), + Error::::InsufficientUnclaimedRedemption + ); + + assert_ok!(Currencies::deposit(STAKING_CURRENCY_ID, &Homa::account_id(), 3_000_000)); + UnclaimedRedemption::::put(3_000_000); + assert_eq!(Homa::unclaimed_redemption(), 3_000_000); + assert_eq!( + Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), + 3_000_000 + ); + + assert_ok!(Homa::claim_redemption(Origin::signed(BOB), ALICE)); + assert_eq!(Homa::unbondings(&ALICE, 1), 0); + assert_eq!(Homa::unbondings(&ALICE, 2), 0); + assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 3_000_000); + assert_eq!(Homa::unclaimed_redemption(), 0); + assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); + }); +} + +#[test] +fn update_homa_params_works() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Homa::update_homa_params(Origin::signed(ALICE), None, None, None, None, None, None), + BadOrigin + ); + + assert_eq!(Homa::soft_bonded_cap_per_sub_account(), 0); + assert_eq!(Homa::estimated_reward_rate_per_era(), Rate::zero()); + assert_eq!(Homa::mint_threshold(), 0); + assert_eq!(Homa::redeem_threshold(), 0); + assert_eq!(Homa::commission_rate(), Rate::zero()); + assert_eq!(Homa::fast_match_fee_rate(), Rate::zero()); + + assert_ok!(Homa::update_homa_params( + Origin::signed(HomaAdmin::get()), + Some(1_000_000_000), + Some(Rate::saturating_from_rational(1, 10000)), + Some(1_000_000), + Some(10_000_000), + Some(Rate::saturating_from_rational(5, 100)), + Some(Rate::saturating_from_rational(1, 100)), + )); + System::assert_has_event(Event::Homa(crate::Event::SoftBondedCapPerSubAccountUpdated( + 1_000_000_000, + ))); + System::assert_has_event(Event::Homa(crate::Event::EstimatedRewardRatePerEraUpdated( + Rate::saturating_from_rational(1, 10000), + ))); + System::assert_has_event(Event::Homa(crate::Event::MintThresholdUpdated(1_000_000))); + System::assert_has_event(Event::Homa(crate::Event::RedeemThresholdUpdated(10_000_000))); + System::assert_has_event(Event::Homa(crate::Event::CommissionRateUpdated( + Rate::saturating_from_rational(5, 100), + ))); + System::assert_has_event(Event::Homa(crate::Event::FastMatchFeeRateUpdated( + Rate::saturating_from_rational(1, 100), + ))); + assert_eq!(Homa::soft_bonded_cap_per_sub_account(), 1_000_000_000); + assert_eq!( + Homa::estimated_reward_rate_per_era(), + Rate::saturating_from_rational(1, 10000) + ); + assert_eq!(Homa::mint_threshold(), 1_000_000); + assert_eq!(Homa::redeem_threshold(), 10_000_000); + assert_eq!(Homa::commission_rate(), Rate::saturating_from_rational(5, 100)); + assert_eq!(Homa::fast_match_fee_rate(), Rate::saturating_from_rational(1, 100)); + }); +} + +#[test] +fn update_bump_era_params_works() { ExtBuilder::default().build().execute_with(|| { - assert_noop!(Homa::update_ledgers(Origin::signed(ALICE), vec![]), BadOrigin); + assert_noop!( + Homa::update_bump_era_params(Origin::signed(ALICE), None, None), + BadOrigin + ); + assert_eq!(Homa::bump_era_prefix(), 0); + assert_eq!(Homa::bump_era_frequency(), 0); + + assert_ok!(Homa::update_bump_era_params( + Origin::signed(HomaAdmin::get()), + Some(10), + Some(7200), + )); + System::assert_has_event(Event::Homa(crate::Event::BumpEraPrefixUpdated(10))); + System::assert_has_event(Event::Homa(crate::Event::BumpEraFrequencyUpdated(7200))); + assert_eq!(Homa::bump_era_prefix(), 10); + assert_eq!(Homa::bump_era_frequency(), 7200); + }); +} + +#[test] +fn reset_ledgers_works() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!(Homa::reset_ledgers(Origin::signed(ALICE), vec![]), BadOrigin); assert_eq!(Homa::staking_ledgers(0), None); assert_eq!(Homa::staking_ledgers(1), None); - assert_ok!(Homa::update_ledgers( + assert_ok!(Homa::reset_ledgers( Origin::signed(HomaAdmin::get()), vec![ ( @@ -48,15 +328,15 @@ fn update_ledgers_works() { (1, None, Some(vec![UnlockChunk { value: 2000, era: 10 },])), ] )); - System::assert_has_event(Event::Homa(crate::Event::LedgerBondedUpdated(0, 1_000_000))); - System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingUpdated( + System::assert_has_event(Event::Homa(crate::Event::LedgerBondedReset(0, 1_000_000))); + System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingReset( 0, vec![ UnlockChunk { value: 1000, era: 5 }, UnlockChunk { value: 20_000, era: 6 }, ], ))); - System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingUpdated( + System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingReset( 1, vec![UnlockChunk { value: 2000, era: 10 }], ))); @@ -78,76 +358,38 @@ fn update_ledgers_works() { }) ); - assert_ok!(Homa::update_ledgers( + assert_ok!(Homa::reset_ledgers( Origin::signed(HomaAdmin::get()), vec![ (0, None, Some(vec![UnlockChunk { value: 20_000, era: 6 },])), (1, Some(0), Some(vec![])), - ] - )); - System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingUpdated( - 0, - vec![UnlockChunk { value: 20_000, era: 6 }], - ))); - System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingUpdated(1, vec![]))); - assert_eq!( - Homa::staking_ledgers(0), - Some(StakingLedger { - bonded: 1_000_000, - unlocking: vec![UnlockChunk { value: 20_000, era: 6 },] - }) - ); - assert_eq!(Homa::staking_ledgers(1), None); - }); -} - -#[test] -fn update_homa_params_works() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Homa::update_homa_params(Origin::signed(ALICE), None, None, None, None, None, None), - BadOrigin - ); - - assert_eq!(Homa::soft_bonded_cap_per_sub_account(), 0); - assert_eq!(Homa::estimated_reward_rate_per_era(), Rate::zero()); - assert_eq!(Homa::mint_threshold(), 0); - assert_eq!(Homa::redeem_threshold(), 0); - assert_eq!(Homa::commission_rate(), Rate::zero()); - assert_eq!(Homa::fast_match_fee_rate(), Rate::zero()); - - assert_ok!(Homa::update_homa_params( - Origin::signed(HomaAdmin::get()), - Some(1_000_000_000), - Some(Rate::saturating_from_rational(1, 10000)), - Some(1_000_000), - Some(10_000_000), - Some(Rate::saturating_from_rational(5, 100)), - Some(Rate::saturating_from_rational(1, 100)), + ] )); - System::assert_has_event(Event::Homa(crate::Event::SoftBondedCapPerSubAccountUpdated( - 1_000_000_000, - ))); - System::assert_has_event(Event::Homa(crate::Event::EstimatedRewardRatePerEraUpdated( - Rate::saturating_from_rational(1, 10000), - ))); - System::assert_has_event(Event::Homa(crate::Event::MintThresholdUpdated(1_000_000))); - System::assert_has_event(Event::Homa(crate::Event::RedeemThresholdUpdated(10_000_000))); - System::assert_has_event(Event::Homa(crate::Event::CommissionRateUpdated( - Rate::saturating_from_rational(5, 100), - ))); - System::assert_has_event(Event::Homa(crate::Event::FastMatchFeeRateUpdated( - Rate::saturating_from_rational(1, 100), + System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingReset( + 0, + vec![UnlockChunk { value: 20_000, era: 6 }], ))); - assert_eq!(Homa::soft_bonded_cap_per_sub_account(), 1_000_000_000); + System::assert_has_event(Event::Homa(crate::Event::LedgerUnlockingReset(1, vec![]))); assert_eq!( - Homa::estimated_reward_rate_per_era(), - Rate::saturating_from_rational(1, 10000) + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![UnlockChunk { value: 20_000, era: 6 },] + }) ); - assert_eq!(Homa::mint_threshold(), 1_000_000); - assert_eq!(Homa::redeem_threshold(), 10_000_000); - assert_eq!(Homa::commission_rate(), Rate::saturating_from_rational(5, 100)); - assert_eq!(Homa::fast_match_fee_rate(), Rate::saturating_from_rational(1, 100)); + assert_eq!(Homa::staking_ledgers(1), None); + }); +} + +#[test] +fn reset_current_era_works() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!(Homa::reset_current_era(Origin::signed(ALICE), 1), BadOrigin); + assert_eq!(Homa::relay_chain_current_era(), 0); + + assert_ok!(Homa::reset_current_era(Origin::signed(HomaAdmin::get()), 1)); + System::assert_last_event(Event::Homa(crate::Event::CurrentEraReset(1))); + assert_eq!(Homa::relay_chain_current_era(), 1); }); } @@ -164,7 +406,7 @@ fn get_staking_currency_soft_cap_works() { } #[test] -fn get_total_bonded_soft_cap_works() { +fn get_total_bonded_works() { ExtBuilder::default().build().execute_with(|| { StakingLedgers::::insert( 0, @@ -211,310 +453,102 @@ fn get_total_staking_currency_works() { ToBondPool::::put(2_000_000); assert_eq!(Homa::get_total_staking_currency(), 5_000_000); }); -} - -#[test] -fn current_exchange_rate_works() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); - assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_000_000)); - assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(10_000_000)); - - StakingLedgers::::insert( - 0, - StakingLedger { - bonded: 1_000_000, - ..Default::default() - }, - ); - assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); - assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_000_000)); - assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(10_000_000)); - - assert_ok!(Currencies::deposit(LiquidCurrencyId::get(), &ALICE, 5_000_000)); - assert_eq!( - Homa::current_exchange_rate(), - ExchangeRate::saturating_from_rational(1_000_000, 5_000_000) - ); - assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(2_000_000)); - assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(5_000_000)); - - TotalVoidLiquid::::put(3_000_000); - assert_eq!( - Homa::current_exchange_rate(), - ExchangeRate::saturating_from_rational(1_000_000, 8_000_000) - ); - assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_250_000)); - assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(8_000_000)); - }); -} - -#[test] -fn distribution_helpers_works() { - ExtBuilder::default().build().execute_with(|| { - let bonded_list = vec![(0, 1_000_000), (1, 2_000_000), (2, 3_000_000), (3, 100_000)]; - - assert_eq!( - distribute_increment(bonded_list.clone(), 2_000_000, None, None), - (vec![(3, 2_000_000)], 0) - ); - assert_eq!( - distribute_increment(bonded_list.clone(), 2_000_000, Some(1_100_000), None), - (vec![(3, 1_000_000), (0, 100_000)], 900_000) - ); - assert_eq!( - distribute_increment(bonded_list.clone(), 2_000_000, Some(100_000), None), - (vec![], 2_000_000) - ); - assert_eq!( - distribute_increment(bonded_list.clone(), 2_000_000, None, Some(2_000_001)), - (vec![], 2_000_000) - ); - assert_eq!( - distribute_increment(bonded_list.clone(), 2_000_000, Some(1_000_000), Some(900_001)), - (vec![], 2_000_000) - ); - assert_eq!( - distribute_increment(bonded_list.clone(), 2_000_000, Some(1_200_000), Some(1_000_000)), - (vec![(3, 1_100_000)], 900_000) - ); - - assert_eq!( - distribute_decrement(bonded_list.clone(), 7_000_000, None, None), - ( - vec![(2, 3_000_000), (1, 2_000_000), (0, 1_000_000), (3, 100_000)], - 900_000 - ) - ); - assert_eq!( - distribute_decrement(bonded_list.clone(), 2_000_000, Some(1_800_000), None), - (vec![(2, 1_200_000), (1, 200_000)], 600_000) - ); - assert_eq!( - distribute_decrement(bonded_list.clone(), 2_000_000, Some(3_000_000), None), - (vec![], 2_000_000) - ); - assert_eq!( - distribute_decrement(bonded_list.clone(), 6_000_000, None, Some(2_000_000)), - (vec![(2, 3_000_000), (1, 2_000_000)], 1_000_000) - ); - assert_eq!( - distribute_decrement(bonded_list.clone(), 2_000_000, None, Some(3_000_001)), - (vec![], 2_000_000) - ); - assert_eq!( - distribute_decrement(bonded_list.clone(), 3_000_000, Some(1_000_000), Some(1_000_001)), - (vec![(2, 2_000_000)], 1_000_000) - ); - }); -} - -#[test] -fn mint_works() { - ExtBuilder::default() - .balances(vec![ - (ALICE, STAKING_CURRENCY_ID, 1_000_000), - (BOB, STAKING_CURRENCY_ID, 1_000_000), - ]) - .build() - .execute_with(|| { - assert_ok!(Homa::update_homa_params( - Origin::signed(HomaAdmin::get()), - Some(1_000_000), - None, - Some(100_000), - None, - None, - None, - )); - - assert_noop!( - Homa::mint(Origin::signed(ALICE), 99_999), - Error::::BelowMintThreshold - ); - assert_noop!( - Homa::mint(Origin::signed(ALICE), 3_000_001), - Error::::ExceededStakingCurrencySoftCap - ); - assert_noop!( - Homa::mint(Origin::signed(ALICE), 3_000_000), - orml_tokens::Error::::BalanceTooLow - ); - - assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 0); - assert_eq!(Homa::total_void_liquid(), 0); - assert_eq!(Homa::to_bond_pool(), 0); - assert_eq!(Homa::get_total_staking_currency(), 0); - assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 0); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 1_000_000); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); - - assert_ok!(Homa::mint(Origin::signed(ALICE), 500_000)); - System::assert_last_event(Event::Homa(crate::Event::Minted(ALICE, 500_000, 5_000_000, 0))); - - assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 5_000_000); - assert_eq!(Homa::total_void_liquid(), 0); - assert_eq!(Homa::to_bond_pool(), 500_000); - assert_eq!(Homa::get_total_staking_currency(), 500_000); - assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 5_000_000); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 500_000); - assert_eq!( - Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), - 500_000 - ); - - assert_ok!(Homa::update_homa_params( - Origin::signed(HomaAdmin::get()), - None, - Some(Rate::saturating_from_rational(10, 100)), - None, - None, - None, - None, - )); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 0); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 1_000_000); - - assert_ok!(Homa::mint(Origin::signed(BOB), 100_000)); - System::assert_last_event(Event::Homa(crate::Event::Minted(BOB, 100_000, 909_090, 90910))); - - assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 5_909_090); - assert_eq!(Homa::total_void_liquid(), 90910); - assert_eq!(Homa::to_bond_pool(), 600_000); - assert_eq!(Homa::get_total_staking_currency(), 600_000); - assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 909_090); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 900_000); - assert_eq!( - Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), - 600_000 - ); - }); -} - -#[test] -fn request_redeem_works() { - ExtBuilder::default() - .balances(vec![ - (ALICE, LIQUID_CURRENCY_ID, 10_000_000), - (BOB, LIQUID_CURRENCY_ID, 10_000_000), - ]) - .build() - .execute_with(|| { - assert_ok!(Homa::update_homa_params( - Origin::signed(HomaAdmin::get()), - None, - None, - None, - Some(1_000_000), - None, - None, - )); - - assert_noop!( - Homa::request_redeem(Origin::signed(ALICE), 999_999, false), - Error::::BelowRedeemThreshold - ); - - assert_eq!(Homa::redeem_requests(&ALICE), None); - assert_eq!(Homa::redeem_requests(&BOB), None); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 10_000_000); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 10_000_000); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); - - assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 1_000_000, false)); - System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(ALICE, 1_000_000, false))); - assert_eq!(Homa::redeem_requests(&ALICE), Some((1_000_000, false))); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 9_000_000); - assert_eq!( - Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), - 1_000_000 - ); +} - assert_ok!(Homa::request_redeem(Origin::signed(BOB), 10_000_000, true)); - System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(BOB, 10_000_000, true))); - assert_eq!(Homa::redeem_requests(&BOB), Some((10_000_000, true))); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 0); - assert_eq!( - Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), - 11_000_000 - ); +#[test] +fn current_exchange_rate_works() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_000_000)); + assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(10_000_000)); - // Alice overwrite the redeem_request - assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 2_000_000, true)); - System::assert_last_event(Event::Homa(crate::Event::RequestedRedeem(ALICE, 2_000_000, true))); - assert_eq!(Homa::redeem_requests(&ALICE), Some((2_000_000, true))); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &ALICE), 8_000_000); - assert_eq!( - Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), - 12_000_000 - ); + StakingLedgers::::insert( + 0, + StakingLedger { + bonded: 1_000_000, + ..Default::default() + }, + ); + assert_eq!(Homa::current_exchange_rate(), DefaultExchangeRate::get()); + assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_000_000)); + assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(10_000_000)); - // Bob cancel the redeem_request - assert_ok!(Homa::request_redeem(Origin::signed(BOB), 0, false)); - System::assert_last_event(Event::Homa(crate::Event::RedeemRequestCancelled(BOB, 10_000_000))); - assert_eq!(Homa::redeem_requests(&BOB), None); - assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 10_000_000); - assert_eq!( - Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), - 2_000_000 - ); - }); + assert_ok!(Currencies::deposit(LiquidCurrencyId::get(), &ALICE, 5_000_000)); + assert_eq!( + Homa::current_exchange_rate(), + ExchangeRate::saturating_from_rational(1_000_000, 5_000_000) + ); + assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(2_000_000)); + assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(5_000_000)); + + TotalVoidLiquid::::put(3_000_000); + assert_eq!( + Homa::current_exchange_rate(), + ExchangeRate::saturating_from_rational(1_000_000, 8_000_000) + ); + assert_eq!(Homa::convert_liquid_to_staking(10_000_000), Ok(1_250_000)); + assert_eq!(Homa::convert_staking_to_liquid(1_000_000), Ok(8_000_000)); + }); } #[test] -fn claim_redemption_works() { - ExtBuilder::default() - .balances(vec![ - (ALICE, LIQUID_CURRENCY_ID, 10_000_000), - (BOB, LIQUID_CURRENCY_ID, 10_000_000), - ]) - .build() - .execute_with(|| { - assert_eq!(Homa::relay_chain_current_era(), 0); - Unbondings::::insert(&ALICE, 1, 1_000_000); - Unbondings::::insert(&ALICE, 2, 2_000_000); - Unbondings::::insert(&ALICE, 3, 3_000_000); - assert_eq!(Homa::unbondings(&ALICE, 1), 1_000_000); - assert_eq!(Homa::unbondings(&ALICE, 2), 2_000_000); - assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); - - // no available expired redemption, nothing happend. - assert_ok!(Homa::claim_redemption(Origin::signed(BOB), ALICE)); - assert_eq!(Homa::unbondings(&ALICE, 1), 1_000_000); - assert_eq!(Homa::unbondings(&ALICE, 2), 2_000_000); - assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0); - assert_eq!(Homa::unclaimed_redemption(), 0); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); - - // there is available expired redemption, but UnclaimedRedemption is not enought. - RelayChainCurrentEra::::put(2); - assert_noop!( - Homa::claim_redemption(Origin::signed(BOB), ALICE), - Error::::InsufficientUnclaimedRedemption - ); +fn distribution_helpers_works() { + ExtBuilder::default().build().execute_with(|| { + let bonded_list = vec![(0, 1_000_000), (1, 2_000_000), (2, 3_000_000), (3, 100_000)]; - assert_ok!(Currencies::deposit(STAKING_CURRENCY_ID, &Homa::account_id(), 3_000_000)); - UnclaimedRedemption::::put(3_000_000); - assert_eq!(Homa::unclaimed_redemption(), 3_000_000); - assert_eq!( - Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), - 3_000_000 - ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, None, None), + (vec![(3, 2_000_000)], 0) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, Some(1_100_000), None), + (vec![(3, 1_000_000), (0, 100_000)], 900_000) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, Some(100_000), None), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, None, Some(2_000_001)), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, Some(1_000_000), Some(900_001)), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_increment(bonded_list.clone(), 2_000_000, Some(1_200_000), Some(1_000_000)), + (vec![(3, 1_100_000)], 900_000) + ); - assert_ok!(Homa::claim_redemption(Origin::signed(BOB), ALICE)); - assert_eq!(Homa::unbondings(&ALICE, 1), 0); - assert_eq!(Homa::unbondings(&ALICE, 2), 0); - assert_eq!(Homa::unbondings(&ALICE, 3), 3_000_000); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 3_000_000); - assert_eq!(Homa::unclaimed_redemption(), 0); - assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); - }); + assert_eq!( + distribute_decrement(bonded_list.clone(), 7_000_000, None, None), + ( + vec![(2, 3_000_000), (1, 2_000_000), (0, 1_000_000), (3, 100_000)], + 900_000 + ) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 2_000_000, Some(1_800_000), None), + (vec![(2, 1_200_000), (1, 200_000)], 600_000) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 2_000_000, Some(3_000_000), None), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 6_000_000, None, Some(2_000_000)), + (vec![(2, 3_000_000), (1, 2_000_000)], 1_000_000) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 2_000_000, None, Some(3_000_001)), + (vec![], 2_000_000) + ); + assert_eq!( + distribute_decrement(bonded_list.clone(), 3_000_000, Some(1_000_000), Some(1_000_001)), + (vec![(2, 2_000_000)], 1_000_000) + ); + }); } #[test] @@ -527,7 +561,7 @@ fn do_fast_match_redeem_works() { ]) .build() .execute_with(|| { - assert_ok!(Homa::update_ledgers( + assert_ok!(Homa::reset_ledgers( Origin::signed(HomaAdmin::get()), vec![(0, Some(4_000_000), None)] )); @@ -622,58 +656,93 @@ fn do_fast_match_redeem_works() { } #[test] -fn draw_staking_reward_works() { +fn process_staking_rewards_works() { ExtBuilder::default() - .balances(vec![ - (ALICE, LIQUID_CURRENCY_ID, 40_000_000), - (CHARLIE, STAKING_CURRENCY_ID, 1_000_000), - ]) + .balances(vec![(ALICE, LIQUID_CURRENCY_ID, 40_000_000)]) .build() .execute_with(|| { - assert_ok!(Homa::update_ledgers( + assert_ok!(Homa::reset_ledgers( Origin::signed(HomaAdmin::get()), - vec![(0, Some(4_000_000), None)] + vec![(0, Some(3_000_000), None), (1, Some(1_000_000), None),] )); assert_ok!(Homa::update_homa_params( Origin::signed(HomaAdmin::get()), - Some(5_000_000), + None, + Some(Rate::saturating_from_rational(20, 100)), None, None, None, - Some(Rate::saturating_from_rational(10, 100)), None, )); - assert_ok!(Homa::mint(Origin::signed(CHARLIE), 1_000_000)); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 3_000_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 1_000_000, + unlocking: vec![] + }) + ); assert_eq!(Homa::get_total_bonded(), 4_000_000); - assert_eq!(Homa::get_total_staking_currency(), 5_000_000); - assert_eq!(Homa::to_bond_pool(), 1_000_000); - assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 50_000_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 40_000_000); assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), 0); - assert_ok!(Homa::draw_staking_reward(2, 1)); - assert_eq!(Homa::get_total_bonded(), 4_000_000); - assert_eq!(Homa::get_total_staking_currency(), 5_000_000); - assert_eq!(Homa::to_bond_pool(), 1_000_000); - assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 50_000_000); + // accumulate staking rewards, no commission + assert_ok!(Homa::process_staking_rewards(1, 0)); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 3_600_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 1_200_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::get_total_bonded(), 4_800_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 40_000_000); assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), 0); assert_ok!(Homa::update_homa_params( Origin::signed(HomaAdmin::get()), None, - Some(Rate::saturating_from_rational(20, 100)), None, None, None, + Some(Rate::saturating_from_rational(10, 100)), None, )); - assert_ok!(Homa::draw_staking_reward(3, 2)); - assert_eq!(Homa::get_total_bonded(), 4_000_000); - assert_eq!(Homa::get_total_staking_currency(), 5_000_000); - assert_eq!(Homa::to_bond_pool(), 1_000_000); - assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 50_699_300); + + // accumulate staking rewards, will draw commission to TreasuryAccount + assert_ok!(Homa::process_staking_rewards(2, 1)); + assert_eq!( + Homa::staking_ledgers(0), + Some(StakingLedger { + bonded: 4_320_000, + unlocking: vec![] + }) + ); + assert_eq!( + Homa::staking_ledgers(1), + Some(StakingLedger { + bonded: 1_440_000, + unlocking: vec![] + }) + ); + assert_eq!(Homa::get_total_bonded(), 5_760_000); + assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 40_677_966); assert_eq!( Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), - 699_300 + 677_966 ); }); } @@ -681,7 +750,7 @@ fn draw_staking_reward_works() { #[test] fn process_scheduled_unbond_works() { ExtBuilder::default().build().execute_with(|| { - assert_ok!(Homa::update_ledgers( + assert_ok!(Homa::reset_ledgers( Origin::signed(HomaAdmin::get()), vec![ ( @@ -786,7 +855,7 @@ fn process_to_bond_pool_works() { None, None, )); - assert_ok!(Homa::update_ledgers( + assert_ok!(Homa::reset_ledgers( Origin::signed(HomaAdmin::get()), vec![(0, Some(1_000_000), None)] )); @@ -810,7 +879,7 @@ fn process_to_bond_pool_works() { ); // ToBondPool is unable to afford xcm_transfer_fee - assert_ok!(Homa::process_to_bond_pool(1)); + assert_ok!(Homa::process_to_bond_pool()); assert_eq!( Homa::staking_ledgers(0), Some(StakingLedger { @@ -836,7 +905,7 @@ fn process_to_bond_pool_works() { Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 1_000_000 ); - assert_ok!(Homa::process_to_bond_pool(2)); + assert_ok!(Homa::process_to_bond_pool()); assert_eq!( Homa::staking_ledgers(0), Some(StakingLedger { @@ -859,7 +928,7 @@ fn process_to_bond_pool_works() { Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 6_000_000 ); - assert_ok!(Homa::process_to_bond_pool(3)); + assert_ok!(Homa::process_to_bond_pool()); assert_eq!( Homa::staking_ledgers(0), Some(StakingLedger { @@ -904,7 +973,7 @@ fn process_to_bond_pool_works() { Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 2_000_000 ); - assert_ok!(Homa::process_to_bond_pool(4)); + assert_ok!(Homa::process_to_bond_pool()); assert_eq!(Homa::to_bond_pool(), 2_000_000); assert_eq!(Homa::get_total_bonded(), 5_000_000); assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 13_000_000); @@ -926,7 +995,7 @@ fn process_redeem_requests_works() { ]) .build() .execute_with(|| { - assert_ok!(Homa::update_ledgers( + assert_ok!(Homa::reset_ledgers( Origin::signed(HomaAdmin::get()), vec![(0, Some(2_000_000), None), (1, Some(3_000_000), None),] )); @@ -1049,7 +1118,33 @@ fn process_redeem_requests_works() { } #[test] -fn bump_new_era_works() { +fn should_bump_local_current_era_works() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(Homa::bump_era_prefix(), 0); + assert_eq!(Homa::bump_era_frequency(), 0); + assert_eq!(Homa::should_bump_local_current_era(10), false); + assert_eq!(Homa::should_bump_local_current_era(12), false); + + assert_ok!(Homa::update_bump_era_params( + Origin::signed(HomaAdmin::get()), + None, + Some(5) + )); + assert_eq!(Homa::should_bump_local_current_era(10), true); + assert_eq!(Homa::should_bump_local_current_era(12), false); + + assert_ok!(Homa::update_bump_era_params( + Origin::signed(HomaAdmin::get()), + Some(3), + None + )); + assert_eq!(Homa::should_bump_local_current_era(10), false); + assert_eq!(Homa::should_bump_local_current_era(12), true); + }); +} + +#[test] +fn bump_current_era_works() { ExtBuilder::default() .balances(vec![(ALICE, STAKING_CURRENCY_ID, 100_000_000)]) .build() @@ -1088,19 +1183,11 @@ fn bump_new_era_works() { 30_000_000 ); - // bump era failed when caller has no permission. - assert_noop!(Homa::bump_current_era(Origin::signed(ALICE), 1), BadOrigin); - - // bump era failed when new era is outdated. - assert_noop!( - Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 0), - Error::::OutdatedEraIndex - ); - // bump era to #1, // will process to_bond_pool. - assert_ok!(Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 1)); - System::assert_last_event(Event::Homa(crate::Event::CurrentEraBumped(1))); + assert_ok!(Homa::bump_current_era()); + System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(1))); + assert_eq!(Homa::relay_chain_current_era(), 1); assert_eq!( Homa::staking_ledgers(0), Some(StakingLedger { @@ -1125,35 +1212,15 @@ fn bump_new_era_works() { assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &TreasuryAccount::get()), 0); - // mock update the staking reward to StakingLedgers - assert_ok!(Homa::update_ledgers( - Origin::signed(HomaAdmin::get()), - vec![(0, Some(20_300_000), None), (1, Some(8_080_000), None),] - )); - assert_eq!( - Homa::staking_ledgers(0), - Some(StakingLedger { - bonded: 20_300_000, - unlocking: vec![] - }) - ); - assert_eq!( - Homa::staking_ledgers(1), - Some(StakingLedger { - bonded: 8_080_000, - unlocking: vec![] - }) - ); - assert_eq!(Homa::get_total_staking_currency(), 28_380_000); - // bump era to #2, - // commission drawn from the staking reward is not zero - assert_ok!(Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 2)); - System::assert_last_event(Event::Homa(crate::Event::CurrentEraBumped(2))); + // accumulate staking reward and draw commission + assert_ok!(Homa::bump_current_era()); + System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(2))); + assert_eq!(Homa::relay_chain_current_era(), 2); assert_eq!( Homa::staking_ledgers(0), Some(StakingLedger { - bonded: 20_300_000, + bonded: 20_200_000, unlocking: vec![] }) ); @@ -1168,7 +1235,7 @@ fn bump_new_era_works() { assert_eq!(Homa::to_bond_pool(), 0); assert_eq!(Homa::unclaimed_redemption(), 0); assert_eq!(Homa::total_void_liquid(), 0); - assert_eq!(Homa::get_total_staking_currency(), 28_380_000); + assert_eq!(Homa::get_total_staking_currency(), 28_280_000); assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 297_619_046); assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); @@ -1197,20 +1264,21 @@ fn bump_new_era_works() { // bump era to #3, // will process redeem requests - assert_ok!(Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 3)); - System::assert_last_event(Event::Homa(crate::Event::CurrentEraBumped(3))); + assert_ok!(Homa::bump_current_era()); + System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(3))); System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( ALICE, 3, 280_000_000, - 26_699_904, + 26_605_824, ))); + assert_eq!(Homa::relay_chain_current_era(), 3); assert_eq!( Homa::staking_ledgers(0), Some(StakingLedger { bonded: 0, unlocking: vec![UnlockChunk { - value: 20_300_000, + value: 20_200_000, era: 3 + BondingDuration::get() }] }) @@ -1218,9 +1286,9 @@ fn bump_new_era_works() { assert_eq!( Homa::staking_ledgers(1), Some(StakingLedger { - bonded: 1_680_096, + bonded: 1_674_176, unlocking: vec![UnlockChunk { - value: 6_399_904, + value: 6_405_824, era: 3 + BondingDuration::get() }] }) @@ -1229,7 +1297,7 @@ fn bump_new_era_works() { assert_eq!(Homa::to_bond_pool(), 0); assert_eq!(Homa::unclaimed_redemption(), 0); assert_eq!(Homa::total_void_liquid(), 0); - assert_eq!(Homa::get_total_staking_currency(), 1_680_096); + assert_eq!(Homa::get_total_staking_currency(), 1_674_176); assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 17_619_046); assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); @@ -1240,25 +1308,28 @@ fn bump_new_era_works() { // bump era to #31, // will process scheduled unbonded - assert_ok!(Homa::bump_current_era(Origin::signed(HomaAdmin::get()), 31)); - System::assert_last_event(Event::Homa(crate::Event::CurrentEraBumped(31))); + for _ in 3..31 { + assert_ok!(Homa::bump_current_era()); + } + System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(31))); + assert_eq!(Homa::relay_chain_current_era(), 31); assert_eq!(Homa::staking_ledgers(0), None); assert_eq!( Homa::staking_ledgers(1), Some(StakingLedger { - bonded: 1_680_096, + bonded: 1_674_176, unlocking: vec![] }) ); assert_eq!(Homa::staking_ledgers(2), None); assert_eq!(Homa::to_bond_pool(), 0); - assert_eq!(Homa::unclaimed_redemption(), 26_699_904); + assert_eq!(Homa::unclaimed_redemption(), 26_605_824); assert_eq!(Homa::total_void_liquid(), 0); - assert_eq!(Homa::get_total_staking_currency(), 1_680_096); + assert_eq!(Homa::get_total_staking_currency(), 1_674_176); assert_eq!(Currencies::total_issuance(LIQUID_CURRENCY_ID), 17_619_046); assert_eq!( Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), - 26_699_904 + 26_605_824 ); assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &Homa::account_id()), 0); assert_eq!( From 20959987e1fd87576d0034a9e91d3875540c45d5 Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Tue, 7 Dec 2021 23:42:44 +1300 Subject: [PATCH 10/27] Added integration test for homa-xcm operations (#1678) * Added integration test for homa-xcm operations Calibrated for xcm weights and fees * Re-calibrated weight and fee * Corrected weight * Added Homa actions to the integration tests. Co-authored-by: Roy Yang --- Cargo.lock | 2 + runtime/integration-tests/Cargo.toml | 2 + runtime/integration-tests/src/homa_xcm.rs | 392 ++++++++++++++++++++++ runtime/integration-tests/src/lib.rs | 5 + runtime/integration-tests/src/setup.rs | 2 +- 5 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 runtime/integration-tests/src/homa_xcm.rs diff --git a/Cargo.lock b/Cargo.lock index 23b0e41396..395202b9fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9921,7 +9921,9 @@ dependencies = [ "module-evm-accounts", "module-evm-bridge", "module-evm-rpc-runtime-api", + "module-homa", "module-homa-lite", + "module-homa-xcm", "module-honzon", "module-incentives", "module-loans", diff --git a/runtime/integration-tests/Cargo.toml b/runtime/integration-tests/Cargo.toml index 287fa1a2e1..ceb1606585 100644 --- a/runtime/integration-tests/Cargo.toml +++ b/runtime/integration-tests/Cargo.toml @@ -104,6 +104,8 @@ module-prices = { path = "../../modules/prices" } module-incentives = { path = "../../modules/incentives" } module-support = { path = "../../modules/support" } module-homa-lite = { path = "../../modules/homa-lite" } +module-homa-xcm = {path = "../../modules/homa-xcm" } +module-homa = {path = "../../modules/homa" } module-session-manager = { path = "../../modules/session-manager" } module-relaychain = {path = "../../modules/relaychain" } primitives = { package = "acala-primitives", path = "../../primitives" } diff --git a/runtime/integration-tests/src/homa_xcm.rs b/runtime/integration-tests/src/homa_xcm.rs new file mode 100644 index 0000000000..f8685ff5dc --- /dev/null +++ b/runtime/integration-tests/src/homa_xcm.rs @@ -0,0 +1,392 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2021 Acala Foundation. +// 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 . + +//! Tests the Homa and HomaXcm module - cross-chain functionalities for the Homa module. + +use crate::setup::*; +use crate::relaychain::kusama_test_net::*; +use frame_support::{assert_ok, weights::Weight}; +use sp_runtime::MultiAddress; +use xcm_emulator::TestExt; +use pallet_staking::StakingLedger; +use module_homa_xcm::HomaXcmOperation; +use module_homa::UnlockChunk; + +// Weight and fee cost is related to the XCM_WEIGHT passed in. +const XCM_WEIGHT: Weight = 10_000_000_000; +const XCM_FEE: Balance = 10_000_000_000; + +fn get_xcm_weight() -> Vec<(HomaXcmOperation, Option, Option)> { + vec![ + // Xcm weight = 400_000_000, fee = 373_333_310 + (HomaXcmOperation::XtokensTransfer, Some(XCM_WEIGHT), Some(XCM_FEE)), + // Xcm weight = 14_000_000_000, fee = 373_333_310 + (HomaXcmOperation::XcmWithdrawUnbonded, Some(XCM_WEIGHT), Some(XCM_FEE)), + // Xcm weight = 14_000_000_000, fee = 373_333_310 + (HomaXcmOperation::XcmBondExtra, Some(XCM_WEIGHT), Some(XCM_FEE)), + // Xcm weight = 14_000_000_000, fee = 373_333_310 + (HomaXcmOperation::XcmUnbond, Some(XCM_WEIGHT), Some(XCM_FEE)), + ] +} + +struct HomaParams { + pub soft_bonded_cap_per_sub_account: Option, + pub estimated_reward_rate_per_era: Option, + pub mint_threshold: Option, + pub redeem_threshold: Option, + pub commission_rate: Option, + pub fast_match_fee_rate: Option, +} +impl Default for HomaParams { + fn default() -> Self { + HomaParams { + soft_bonded_cap_per_sub_account: Some(1_000_000_000 * dollar(RELAY_CHAIN_CURRENCY)), + estimated_reward_rate_per_era: None, + mint_threshold: None, + redeem_threshold: None, + commission_rate: None, + fast_match_fee_rate: None, + } + } +} + +// Helper function to setup config. Called within Karura Externalities. +fn configure_homa_and_homa_xcm() { + // Configure Homa and HomaXcm + assert_ok!(HomaXcm::update_xcm_dest_weight_and_fee(Origin:: root(), get_xcm_weight())); + let param = HomaParams::default(); + assert_ok!(Homa::update_homa_params( + Origin::root(), + param.soft_bonded_cap_per_sub_account, + param.estimated_reward_rate_per_era, + param.mint_threshold, + param.redeem_threshold, + param.commission_rate, + param.fast_match_fee_rate, + )); +} + + +#[test] +fn homa_xcm_transfer_staking_to_sub_account_works() { + let homa_lite_sub_account: AccountId = + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + let mut parachain_account: AccountId = AccountId::default(); + Karura::execute_with(|| { + parachain_account = ParachainAccount::get(); + }); + KusamaNet::execute_with(|| { + // Transfer some KSM into the parachain. + assert_ok!(kusama_runtime::XcmPallet::reserve_transfer_assets( + kusama_runtime::Origin::signed(ALICE.into()), + Box::new(Parachain(2000).into().into()), + Box::new( + Junction::AccountId32 { + id: alice().into(), + network: NetworkId::Any + } + .into() + .into() + ), + Box::new((Here, 2001 * dollar(RELAY_CHAIN_CURRENCY)).into()), + 0 + )); + + assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 0); + assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2003 * dollar(RELAY_CHAIN_CURRENCY)); + }); + + Karura::execute_with(|| { + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(bob())), + RELAY_CHAIN_CURRENCY, + 1_000_000 * dollar(RELAY_CHAIN_CURRENCY), + 0 + )); + + configure_homa_and_homa_xcm(); + + // Transfer fund via XCM by Mint + assert_ok!(Homa::mint(Origin::signed(bob()), 1_000 * dollar(RELAY_CHAIN_CURRENCY))); + assert_ok!(Homa::process_to_bond_pool(0)); + }); + + KusamaNet::execute_with(|| { + // 1000 dollars (minus fee) are transferred into the Kusama chain + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account), + 999_999_893_333_340 + ); + // XCM fee is paid by the parachain account. + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account), + 1003 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + ); + }); +} + +#[test] +fn homa_xcm_withdraw_unbonded_from_sub_account_works() { + let homa_lite_sub_account: AccountId = + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + let mut parachain_account: AccountId = AccountId::default(); + Karura::execute_with(|| { + parachain_account = ParachainAccount::get(); + }); + KusamaNet::execute_with(|| { + kusama_runtime::Staking::trigger_new_era(0, vec![]); + + // Transfer some KSM into the parachain. + assert_ok!(kusama_runtime::Balances::transfer( + kusama_runtime::Origin::signed(ALICE.into()), + MultiAddress::Id(homa_lite_sub_account.clone()), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + )); + + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account.clone()), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + ); + + // bond and unbond some fund for staking + assert_ok!(kusama_runtime::Staking::bond( + kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), + MultiAddress::Id(homa_lite_sub_account.clone()), + 1_000 * dollar(RELAY_CHAIN_CURRENCY), + pallet_staking::RewardDestination::::Staked, + )); + + kusama_runtime::System::set_block_number(100); + assert_ok!(kusama_runtime::Staking::unbond( + kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), + 1_000 * dollar(RELAY_CHAIN_CURRENCY) + )); + + // Kusama's unbonding period is 27 days = 100_800 blocks + kusama_runtime::System::set_block_number(101_000); + for _i in 0..29 { + kusama_runtime::Staking::trigger_new_era(0, vec![]); + } + + // Endowed from kusama_ext() + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account.clone()), + 2 * dollar(RELAY_CHAIN_CURRENCY) + ); + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account.clone()), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + ); + }); + + Karura::execute_with(|| { + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(bob())), + LIQUID_CURRENCY, + 1_000_000 * dollar(LIQUID_CURRENCY), + 0 + )); + + configure_homa_and_homa_xcm(); + + // Add an unlock chunk to the ledger + assert_ok!(Homa::update_ledgers( + Origin::root(), + vec![ + ( + 0, + Some(1_000 * dollar(RELAY_CHAIN_CURRENCY)), + Some(vec![ + UnlockChunk { value: 1000 * dollar(RELAY_CHAIN_CURRENCY), era: 0 }, + ]) + ), + ] + )); + + // Process the unlocking and withdraw unbonded. + assert_ok!(Homa::process_scheduled_unbond(0)); + }); + + KusamaNet::execute_with(|| { + // Fund has been withdrew and transferred. + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account), + dollar(RELAY_CHAIN_CURRENCY) + ); + // Final parachain balance is: unbond_withdrew($1000) + initial_endowment($2) - xcm_fee + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account.clone()), + 1002 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + ); + }); +} + +#[test] +fn homa_xcm_bond_extra_on_sub_account_works() { + let homa_lite_sub_account: AccountId = + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + let mut parachain_account: AccountId = AccountId::default(); + Karura::execute_with(|| { + parachain_account = ParachainAccount::get(); + }); + KusamaNet::execute_with(|| { + assert_ok!(kusama_runtime::Balances::transfer( + kusama_runtime::Origin::signed(ALICE.into()), + MultiAddress::Id(homa_lite_sub_account.clone()), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + )); + + // Bond some money + assert_ok!(kusama_runtime::Staking::bond( + kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), + MultiAddress::Id(homa_lite_sub_account.clone()), + 500 * dollar(RELAY_CHAIN_CURRENCY), + pallet_staking::RewardDestination::::Staked, + )); + + assert_eq!( + kusama_runtime::Staking::ledger(&homa_lite_sub_account), + Some(StakingLedger { + stash: homa_lite_sub_account.clone(), + total: 500 * dollar(RELAY_CHAIN_CURRENCY), + active: 500 * dollar(RELAY_CHAIN_CURRENCY), + unlocking: vec![], + claimed_rewards: vec![], + }) + ); + + assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 1001 * dollar(RELAY_CHAIN_CURRENCY)); + assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2 * dollar(RELAY_CHAIN_CURRENCY)); + }); + + Karura::execute_with(|| { + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(bob())), + RELAY_CHAIN_CURRENCY, + 501 * dollar(RELAY_CHAIN_CURRENCY), + 0 + )); + + configure_homa_and_homa_xcm(); + + // Use Mint to bond more. + assert_ok!(Homa::mint(Origin::signed(bob()), 500 * dollar(RELAY_CHAIN_CURRENCY))); + assert_ok!(Homa::process_to_bond_pool(0)); + }); + + KusamaNet::execute_with(|| { + assert_eq!( + kusama_runtime::Staking::ledger(&homa_lite_sub_account), + Some(StakingLedger { + stash: homa_lite_sub_account.clone(), + total: 1000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE, + active: 1000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE, + unlocking: vec![], + claimed_rewards: vec![], + }) + ); + assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 1001 * dollar(RELAY_CHAIN_CURRENCY)); + // XCM fee is paid by the sovereign account. + assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310); + }); +} + + +#[test] +fn homa_xcm_unbond_on_sub_account_works() { + let homa_lite_sub_account: AccountId = + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + let mut parachain_account: AccountId = AccountId::default(); + Karura::execute_with(|| { + parachain_account = ParachainAccount::get(); + }); + KusamaNet::execute_with(|| { + assert_ok!(kusama_runtime::Balances::transfer( + kusama_runtime::Origin::signed(ALICE.into()), + MultiAddress::Id(homa_lite_sub_account.clone()), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + )); + + // Bond some tokens. + assert_ok!(kusama_runtime::Staking::bond( + kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), + MultiAddress::Id(homa_lite_sub_account.clone()), + dollar(RELAY_CHAIN_CURRENCY), + pallet_staking::RewardDestination::::Staked, + )); + + assert_eq!( + kusama_runtime::Staking::ledger(&homa_lite_sub_account), + Some(StakingLedger { + stash: homa_lite_sub_account.clone(), + total: dollar(RELAY_CHAIN_CURRENCY), + active: dollar(RELAY_CHAIN_CURRENCY), + unlocking: vec![], + claimed_rewards: vec![], + }) + ); + + assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 1_001 * dollar(RELAY_CHAIN_CURRENCY)); + assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2 * dollar(RELAY_CHAIN_CURRENCY)); + }); + + Karura::execute_with(|| { + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(bob())), + RELAY_CHAIN_CURRENCY, + 1_001 * dollar(RELAY_CHAIN_CURRENCY), + 0 + )); + + configure_homa_and_homa_xcm(); + + // Bond more using Mint + // Amount bonded = $1000 - XCM_FEE = 999_990_000_000_000 + assert_ok!(Homa::mint( + Origin::signed(bob()), + 1_000 * dollar(RELAY_CHAIN_CURRENCY), + )); + // Update internal storage in Homa + assert_ok!(Homa::bump_current_era(Origin::root(), 1)); + + // Put in redeem request + assert_ok!(Homa::request_redeem( + Origin::signed(bob()), + 10_000 * dollar(LIQUID_CURRENCY), + false, + )); + // Process the redeem request and unbond funds on the relaychain. + assert_ok!(Homa::process_redeem_requests(1)); + }); + + KusamaNet::execute_with(|| { + // Ensure the correct amount of fund is unbonded + let ledger = kusama_runtime::Staking::ledger(&homa_lite_sub_account).expect("record should exist"); + assert_eq!(ledger.total, 1001 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); + assert_eq!(ledger.active, dollar(RELAY_CHAIN_CURRENCY)); + + assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 1_001 * dollar(RELAY_CHAIN_CURRENCY)); + + // 2 x XCM fee is paid: for Mint and Redeem + assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 * 2); + }); +} \ No newline at end of file diff --git a/runtime/integration-tests/src/lib.rs b/runtime/integration-tests/src/lib.rs index f471cc991e..6445d3ee31 100644 --- a/runtime/integration-tests/src/lib.rs +++ b/runtime/integration-tests/src/lib.rs @@ -50,6 +50,11 @@ mod evm; ))] mod homa_lite; +#[cfg( + feature = "with-karura-runtime", +)] +mod homa_xcm; + #[cfg(any( feature = "with-mandala-runtime", feature = "with-karura-runtime", diff --git a/runtime/integration-tests/src/setup.rs b/runtime/integration-tests/src/setup.rs index 5a15659465..635088db89 100644 --- a/runtime/integration-tests/src/setup.rs +++ b/runtime/integration-tests/src/setup.rs @@ -82,7 +82,7 @@ mod karura_imports { ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, SevenDays, System, Timestamp, TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, - NFT, + NFT, HomaXcm, Homa }; pub use primitives::TradingPair; pub use runtime_common::{cent, dollar, millicent, KAR, KSM, KUSD, LKSM}; From 667f501de305ad71a300b60743261412afd4519d Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Tue, 7 Dec 2021 19:01:01 +0800 Subject: [PATCH 11/27] fix integration tests --- runtime/integration-tests/src/homa_xcm.rs | 286 ++++++++++++---------- 1 file changed, 155 insertions(+), 131 deletions(-) diff --git a/runtime/integration-tests/src/homa_xcm.rs b/runtime/integration-tests/src/homa_xcm.rs index f8685ff5dc..1110823c10 100644 --- a/runtime/integration-tests/src/homa_xcm.rs +++ b/runtime/integration-tests/src/homa_xcm.rs @@ -18,14 +18,14 @@ //! Tests the Homa and HomaXcm module - cross-chain functionalities for the Homa module. -use crate::setup::*; use crate::relaychain::kusama_test_net::*; +use crate::setup::*; use frame_support::{assert_ok, weights::Weight}; +use module_homa::UnlockChunk; +use module_homa_xcm::HomaXcmOperation; +use pallet_staking::StakingLedger; use sp_runtime::MultiAddress; use xcm_emulator::TestExt; -use pallet_staking::StakingLedger; -use module_homa_xcm::HomaXcmOperation; -use module_homa::UnlockChunk; // Weight and fee cost is related to the XCM_WEIGHT passed in. const XCM_WEIGHT: Weight = 10_000_000_000; @@ -34,7 +34,7 @@ const XCM_FEE: Balance = 10_000_000_000; fn get_xcm_weight() -> Vec<(HomaXcmOperation, Option, Option)> { vec![ // Xcm weight = 400_000_000, fee = 373_333_310 - (HomaXcmOperation::XtokensTransfer, Some(XCM_WEIGHT), Some(XCM_FEE)), + (HomaXcmOperation::XtokensTransfer, Some(XCM_WEIGHT), Some(XCM_FEE)), // Xcm weight = 14_000_000_000, fee = 373_333_310 (HomaXcmOperation::XcmWithdrawUnbonded, Some(XCM_WEIGHT), Some(XCM_FEE)), // Xcm weight = 14_000_000_000, fee = 373_333_310 @@ -67,25 +67,27 @@ impl Default for HomaParams { // Helper function to setup config. Called within Karura Externalities. fn configure_homa_and_homa_xcm() { - // Configure Homa and HomaXcm - assert_ok!(HomaXcm::update_xcm_dest_weight_and_fee(Origin:: root(), get_xcm_weight())); - let param = HomaParams::default(); - assert_ok!(Homa::update_homa_params( - Origin::root(), - param.soft_bonded_cap_per_sub_account, - param.estimated_reward_rate_per_era, - param.mint_threshold, - param.redeem_threshold, - param.commission_rate, - param.fast_match_fee_rate, - )); + // Configure Homa and HomaXcm + assert_ok!(HomaXcm::update_xcm_dest_weight_and_fee( + Origin::root(), + get_xcm_weight() + )); + let param = HomaParams::default(); + assert_ok!(Homa::update_homa_params( + Origin::root(), + param.soft_bonded_cap_per_sub_account, + param.estimated_reward_rate_per_era, + param.mint_threshold, + param.redeem_threshold, + param.commission_rate, + param.fast_match_fee_rate, + )); } - #[test] fn homa_xcm_transfer_staking_to_sub_account_works() { let homa_lite_sub_account: AccountId = - hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); let mut parachain_account: AccountId = AccountId::default(); Karura::execute_with(|| { parachain_account = ParachainAccount::get(); @@ -108,7 +110,10 @@ fn homa_xcm_transfer_staking_to_sub_account_works() { )); assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 0); - assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2003 * dollar(RELAY_CHAIN_CURRENCY)); + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account), + 2003 * dollar(RELAY_CHAIN_CURRENCY) + ); }); Karura::execute_with(|| { @@ -124,7 +129,7 @@ fn homa_xcm_transfer_staking_to_sub_account_works() { // Transfer fund via XCM by Mint assert_ok!(Homa::mint(Origin::signed(bob()), 1_000 * dollar(RELAY_CHAIN_CURRENCY))); - assert_ok!(Homa::process_to_bond_pool(0)); + assert_ok!(Homa::process_to_bond_pool()); }); KusamaNet::execute_with(|| { @@ -135,7 +140,7 @@ fn homa_xcm_transfer_staking_to_sub_account_works() { ); // XCM fee is paid by the parachain account. assert_eq!( - kusama_runtime::Balances::free_balance(¶chain_account), + kusama_runtime::Balances::free_balance(¶chain_account), 1003 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 ); }); @@ -144,104 +149,103 @@ fn homa_xcm_transfer_staking_to_sub_account_works() { #[test] fn homa_xcm_withdraw_unbonded_from_sub_account_works() { let homa_lite_sub_account: AccountId = - hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); - let mut parachain_account: AccountId = AccountId::default(); - Karura::execute_with(|| { - parachain_account = ParachainAccount::get(); - }); - KusamaNet::execute_with(|| { + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + let mut parachain_account: AccountId = AccountId::default(); + Karura::execute_with(|| { + parachain_account = ParachainAccount::get(); + }); + KusamaNet::execute_with(|| { + kusama_runtime::Staking::trigger_new_era(0, vec![]); + + // Transfer some KSM into the parachain. + assert_ok!(kusama_runtime::Balances::transfer( + kusama_runtime::Origin::signed(ALICE.into()), + MultiAddress::Id(homa_lite_sub_account.clone()), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + )); + + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account.clone()), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + ); + + // bond and unbond some fund for staking + assert_ok!(kusama_runtime::Staking::bond( + kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), + MultiAddress::Id(homa_lite_sub_account.clone()), + 1_000 * dollar(RELAY_CHAIN_CURRENCY), + pallet_staking::RewardDestination::::Staked, + )); + + kusama_runtime::System::set_block_number(100); + assert_ok!(kusama_runtime::Staking::unbond( + kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), + 1_000 * dollar(RELAY_CHAIN_CURRENCY) + )); + + // Kusama's unbonding period is 27 days = 100_800 blocks + kusama_runtime::System::set_block_number(101_000); + for _i in 0..29 { kusama_runtime::Staking::trigger_new_era(0, vec![]); + } + + // Endowed from kusama_ext() + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account.clone()), + 2 * dollar(RELAY_CHAIN_CURRENCY) + ); + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account.clone()), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + ); + }); - // Transfer some KSM into the parachain. - assert_ok!(kusama_runtime::Balances::transfer( - kusama_runtime::Origin::signed(ALICE.into()), - MultiAddress::Id(homa_lite_sub_account.clone()), - 1_001 * dollar(RELAY_CHAIN_CURRENCY) - )); - - assert_eq!( - kusama_runtime::Balances::free_balance(&homa_lite_sub_account.clone()), - 1_001 * dollar(RELAY_CHAIN_CURRENCY) - ); - - // bond and unbond some fund for staking - assert_ok!(kusama_runtime::Staking::bond( - kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), - MultiAddress::Id(homa_lite_sub_account.clone()), - 1_000 * dollar(RELAY_CHAIN_CURRENCY), - pallet_staking::RewardDestination::::Staked, - )); - - kusama_runtime::System::set_block_number(100); - assert_ok!(kusama_runtime::Staking::unbond( - kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), - 1_000 * dollar(RELAY_CHAIN_CURRENCY) - )); - - // Kusama's unbonding period is 27 days = 100_800 blocks - kusama_runtime::System::set_block_number(101_000); - for _i in 0..29 { - kusama_runtime::Staking::trigger_new_era(0, vec![]); - } - - // Endowed from kusama_ext() - assert_eq!( - kusama_runtime::Balances::free_balance(¶chain_account.clone()), - 2 * dollar(RELAY_CHAIN_CURRENCY) - ); - assert_eq!( - kusama_runtime::Balances::free_balance(&homa_lite_sub_account.clone()), - 1_001 * dollar(RELAY_CHAIN_CURRENCY) - ); - }); - - Karura::execute_with(|| { - assert_ok!(Tokens::set_balance( - Origin::root(), - MultiAddress::Id(AccountId::from(bob())), - LIQUID_CURRENCY, - 1_000_000 * dollar(LIQUID_CURRENCY), - 0 - )); - - configure_homa_and_homa_xcm(); - - // Add an unlock chunk to the ledger - assert_ok!(Homa::update_ledgers( - Origin::root(), - vec![ - ( - 0, - Some(1_000 * dollar(RELAY_CHAIN_CURRENCY)), - Some(vec![ - UnlockChunk { value: 1000 * dollar(RELAY_CHAIN_CURRENCY), era: 0 }, - ]) - ), - ] - )); - - // Process the unlocking and withdraw unbonded. - assert_ok!(Homa::process_scheduled_unbond(0)); - }); - - KusamaNet::execute_with(|| { - // Fund has been withdrew and transferred. - assert_eq!( - kusama_runtime::Balances::free_balance(&homa_lite_sub_account), - dollar(RELAY_CHAIN_CURRENCY) - ); - // Final parachain balance is: unbond_withdrew($1000) + initial_endowment($2) - xcm_fee - assert_eq!( - kusama_runtime::Balances::free_balance(¶chain_account.clone()), - 1002 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 - ); - }); + Karura::execute_with(|| { + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(bob())), + LIQUID_CURRENCY, + 1_000_000 * dollar(LIQUID_CURRENCY), + 0 + )); + + configure_homa_and_homa_xcm(); + + // Add an unlock chunk to the ledger + assert_ok!(Homa::reset_ledgers( + Origin::root(), + vec![( + 0, + Some(1_000 * dollar(RELAY_CHAIN_CURRENCY)), + Some(vec![UnlockChunk { + value: 1000 * dollar(RELAY_CHAIN_CURRENCY), + era: 0 + },]) + ),] + )); + + // Process the unlocking and withdraw unbonded. + assert_ok!(Homa::process_scheduled_unbond(0)); + }); + + KusamaNet::execute_with(|| { + // Fund has been withdrew and transferred. + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account), + dollar(RELAY_CHAIN_CURRENCY) + ); + // Final parachain balance is: unbond_withdrew($1000) + initial_endowment($2) - xcm_fee + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account.clone()), + 1002 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + ); + }); } #[test] fn homa_xcm_bond_extra_on_sub_account_works() { let homa_lite_sub_account: AccountId = - hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); let mut parachain_account: AccountId = AccountId::default(); Karura::execute_with(|| { parachain_account = ParachainAccount::get(); @@ -272,8 +276,14 @@ fn homa_xcm_bond_extra_on_sub_account_works() { }) ); - assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 1001 * dollar(RELAY_CHAIN_CURRENCY)); - assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2 * dollar(RELAY_CHAIN_CURRENCY)); + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account), + 1001 * dollar(RELAY_CHAIN_CURRENCY) + ); + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account), + 2 * dollar(RELAY_CHAIN_CURRENCY) + ); }); Karura::execute_with(|| { @@ -289,7 +299,7 @@ fn homa_xcm_bond_extra_on_sub_account_works() { // Use Mint to bond more. assert_ok!(Homa::mint(Origin::signed(bob()), 500 * dollar(RELAY_CHAIN_CURRENCY))); - assert_ok!(Homa::process_to_bond_pool(0)); + assert_ok!(Homa::process_to_bond_pool()); }); KusamaNet::execute_with(|| { @@ -303,17 +313,22 @@ fn homa_xcm_bond_extra_on_sub_account_works() { claimed_rewards: vec![], }) ); - assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 1001 * dollar(RELAY_CHAIN_CURRENCY)); + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account), + 1001 * dollar(RELAY_CHAIN_CURRENCY) + ); // XCM fee is paid by the sovereign account. - assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310); + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account), + 2 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + ); }); } - #[test] fn homa_xcm_unbond_on_sub_account_works() { let homa_lite_sub_account: AccountId = - hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); let mut parachain_account: AccountId = AccountId::default(); Karura::execute_with(|| { parachain_account = ParachainAccount::get(); @@ -344,10 +359,16 @@ fn homa_xcm_unbond_on_sub_account_works() { }) ); - assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 1_001 * dollar(RELAY_CHAIN_CURRENCY)); - assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2 * dollar(RELAY_CHAIN_CURRENCY)); + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + ); + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account), + 2 * dollar(RELAY_CHAIN_CURRENCY) + ); }); - + Karura::execute_with(|| { assert_ok!(Tokens::set_balance( Origin::root(), @@ -361,12 +382,9 @@ fn homa_xcm_unbond_on_sub_account_works() { // Bond more using Mint // Amount bonded = $1000 - XCM_FEE = 999_990_000_000_000 - assert_ok!(Homa::mint( - Origin::signed(bob()), - 1_000 * dollar(RELAY_CHAIN_CURRENCY), - )); + assert_ok!(Homa::mint(Origin::signed(bob()), 1_000 * dollar(RELAY_CHAIN_CURRENCY),)); // Update internal storage in Homa - assert_ok!(Homa::bump_current_era(Origin::root(), 1)); + assert_ok!(Homa::bump_current_era()); // Put in redeem request assert_ok!(Homa::request_redeem( @@ -384,9 +402,15 @@ fn homa_xcm_unbond_on_sub_account_works() { assert_eq!(ledger.total, 1001 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); assert_eq!(ledger.active, dollar(RELAY_CHAIN_CURRENCY)); - assert_eq!(kusama_runtime::Balances::free_balance(&homa_lite_sub_account), 1_001 * dollar(RELAY_CHAIN_CURRENCY)); + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account), + 1_001 * dollar(RELAY_CHAIN_CURRENCY) + ); // 2 x XCM fee is paid: for Mint and Redeem - assert_eq!(kusama_runtime::Balances::free_balance(¶chain_account), 2 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 * 2); + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account), + 2 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 * 2 + ); }); -} \ No newline at end of file +} From 6f6a414d92bc079db87def1086618b8b7985ac48 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Tue, 7 Dec 2021 19:05:24 +0800 Subject: [PATCH 12/27] format --- runtime/integration-tests/src/lib.rs | 4 +--- runtime/integration-tests/src/setup.rs | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/runtime/integration-tests/src/lib.rs b/runtime/integration-tests/src/lib.rs index 6445d3ee31..cb63919a22 100644 --- a/runtime/integration-tests/src/lib.rs +++ b/runtime/integration-tests/src/lib.rs @@ -50,9 +50,7 @@ mod evm; ))] mod homa_lite; -#[cfg( - feature = "with-karura-runtime", -)] +#[cfg(feature = "with-karura-runtime")] mod homa_xcm; #[cfg(any( diff --git a/runtime/integration-tests/src/setup.rs b/runtime/integration-tests/src/setup.rs index 635088db89..4259d3be90 100644 --- a/runtime/integration-tests/src/setup.rs +++ b/runtime/integration-tests/src/setup.rs @@ -77,12 +77,12 @@ mod karura_imports { AuctionManager, Authority, AuthoritysOriginId, Balance, Balances, BlockNumber, Call, CdpEngine, CdpTreasury, CreateClassDeposit, CreateTokenDeposit, Currencies, CurrencyId, CurrencyIdConvert, DataDepositPerByte, DefaultExchangeRate, Dex, EmergencyShutdown, Event, EvmAccounts, ExistentialDeposits, Get, GetNativeCurrencyId, - HomaLite, Honzon, IdleScheduler, KaruraFoundationAccounts, Loans, MinimumDebitValue, MultiLocation, - NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, ParachainAccount, - ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, + Homa, HomaLite, HomaXcm, Honzon, IdleScheduler, KaruraFoundationAccounts, Loans, MinimumDebitValue, + MultiLocation, NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, + ParachainAccount, ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, SevenDays, System, Timestamp, TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, - NFT, HomaXcm, Homa + NFT, }; pub use primitives::TradingPair; pub use runtime_common::{cent, dollar, millicent, KAR, KSM, KUSD, LKSM}; From 4afdf022c6a03a1d2f58982ba0beaa025c0a1cf0 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Wed, 8 Dec 2021 19:02:43 +0800 Subject: [PATCH 13/27] add benchmarking tests for homa --- Cargo.lock | 1 + modules/homa-xcm/src/lib.rs | 2 +- modules/homa/src/lib.rs | 27 ++-- modules/homa/src/mock.rs | 1 + modules/homa/src/weights.rs | 169 +++++++++++++++++++++++ runtime/karura/src/benchmarking/mod.rs | 3 + runtime/karura/src/lib.rs | 15 +- runtime/mandala/Cargo.toml | 5 + runtime/mandala/src/benchmarking/homa.rs | 164 ++++++++++++++++++++++ runtime/mandala/src/benchmarking/mod.rs | 1 + runtime/mandala/src/lib.rs | 47 ++++++- 11 files changed, 414 insertions(+), 21 deletions(-) create mode 100644 modules/homa/src/weights.rs create mode 100644 runtime/mandala/src/benchmarking/homa.rs diff --git a/Cargo.lock b/Cargo.lock index 395202b9fe..e2d44bf650 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4955,6 +4955,7 @@ dependencies = [ "module-evm-utiltity", "module-homa", "module-homa-lite", + "module-homa-xcm", "module-honzon", "module-idle-scheduler", "module-incentives", diff --git a/modules/homa-xcm/src/lib.rs b/modules/homa-xcm/src/lib.rs index e2c1c9f808..e0d42f8182 100644 --- a/modules/homa-xcm/src/lib.rs +++ b/modules/homa-xcm/src/lib.rs @@ -111,7 +111,7 @@ pub mod module { /// /// Parameters: /// - `updates`: tumple of (HomaXcmOperation, WeightChange, FeeChange). - #[pallet::weight(10_000)] + #[pallet::weight(10_000_000)] #[transactional] pub fn update_xcm_dest_weight_and_fee( origin: OriginFor, diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 5781fc1e95..326cfae2b8 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -35,9 +35,11 @@ use sp_runtime::{ use sp_std::{cmp::Ordering, convert::From, prelude::*, vec, vec::Vec}; pub use module::*; +pub use weights::WeightInfo; mod mock; mod tests; +pub mod weights; #[frame_support::pallet] pub mod module { @@ -133,6 +135,9 @@ pub mod module { /// The HomaXcm to manage the staking of sub-account on relaychain. type HomaXcm: HomaSubAccountXcm; + + /// Weight information for the extrinsics in this module. + type WeightInfo: WeightInfo; } #[pallet::error] @@ -321,12 +326,12 @@ pub mod module { #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(n: T::BlockNumber) -> Weight { - let current_weight = 0; if Self::should_bump_local_current_era(n) { let _ = Self::bump_current_era(); + ::WeightInfo::on_initialize_with_bump_era() + } else { + ::WeightInfo::on_initialize() } - - current_weight } } @@ -336,7 +341,7 @@ pub mod module { /// /// Parameters: /// - `amount`: The amount of staking currency used to mint liquid currency. - #[pallet::weight(10_000)] + #[pallet::weight(< T as Config >::WeightInfo::mint())] #[transactional] pub fn mint(origin: OriginFor, #[pallet::compact] amount: Balance) -> DispatchResult { let minter = ensure_signed(origin)?; @@ -388,7 +393,7 @@ pub mod module { /// currency. /// - `allow_fast_match`: allow the request to be fast matched, fast match will take a fixed /// rate as fee. - #[pallet::weight(10_000)] + #[pallet::weight(< T as Config >::WeightInfo::request_redeem())] #[transactional] pub fn request_redeem( origin: OriginFor, @@ -445,7 +450,7 @@ pub mod module { /// /// Parameters: /// - `redeemer_list`: The list of redeem requests to execute fast redeem. - #[pallet::weight(10_000)] + #[pallet::weight(< T as Config >::WeightInfo::fast_match_redeems(redeemer_list.len() as u32))] #[transactional] pub fn fast_match_redeems(origin: OriginFor, redeemer_list: Vec) -> DispatchResult { let _ = ensure_signed(origin)?; @@ -461,7 +466,7 @@ pub mod module { /// /// Parameters: /// - `redeemer`: redeemer. - #[pallet::weight(10_000)] + #[pallet::weight(< T as Config >::WeightInfo::claim_redemption())] #[transactional] pub fn claim_redemption(origin: OriginFor, redeemer: T::AccountId) -> DispatchResult { let _ = ensure_signed(origin)?; @@ -508,7 +513,7 @@ pub mod module { /// - `commission_rate`: the rate to draw from estimated staking rewards as commission to /// HomaTreasury /// - `fast_match_fee_rate`: the fixed fee rate when redeem request is been fast matched. - #[pallet::weight(10_000)] + #[pallet::weight(< T as Config >::WeightInfo::update_homa_params())] #[transactional] pub fn update_homa_params( origin: OriginFor, @@ -555,7 +560,7 @@ pub mod module { /// Parameters: /// - `prefix`: the prefix of block number on parachain. /// - `frequency`: the frequency of block number on parachain. - #[pallet::weight(10_000)] + #[pallet::weight(< T as Config >::WeightInfo::update_bump_era_params())] #[transactional] pub fn update_bump_era_params( origin: OriginFor, @@ -581,7 +586,7 @@ pub mod module { /// /// Parameters: /// - `updates`: update list of subaccount. - #[pallet::weight(10_000)] + #[pallet::weight(< T as Config >::WeightInfo::reset_ledgers(updates.len() as u32))] #[transactional] pub fn reset_ledgers( origin: OriginFor, @@ -618,7 +623,7 @@ pub mod module { /// /// Parameters: /// - `era_index`: the latest era index of relaychain. - #[pallet::weight(10_000)] + #[pallet::weight(< T as Config >::WeightInfo::reset_current_era())] #[transactional] pub fn reset_current_era(origin: OriginFor, era_index: EraIndex) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; diff --git a/modules/homa/src/mock.rs b/modules/homa/src/mock.rs index 44bddfa4d1..3f9469ada6 100644 --- a/modules/homa/src/mock.rs +++ b/modules/homa/src/mock.rs @@ -180,6 +180,7 @@ impl Config for Runtime { type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; type BondingDuration = BondingDuration; type HomaXcm = MockHomaSubAccountXcm; + type WeightInfo = (); } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/modules/homa/src/weights.rs b/modules/homa/src/weights.rs new file mode 100644 index 0000000000..874c54e653 --- /dev/null +++ b/modules/homa/src/weights.rs @@ -0,0 +1,169 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2021 Acala Foundation. +// 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 . + +//! Autogenerated weights for module_homa_lite +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2021-11-24, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("karura-dev"), DB CACHE: 128 + +// Executed Command: +// target/release/acala +// benchmark +// --chain=karura-dev +// --steps=50 +// --repeat=20 +// --pallet=module-homa +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=./templates/module-weight-template.hbs +// --output=./modules/homa/src/weights.rs + + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for module_homa. +pub trait WeightInfo { + fn on_initialize() -> Weight; + fn on_initialize_with_bump_era() -> Weight; + fn mint() -> Weight; + fn request_redeem() -> Weight; + fn fast_match_redeems(u: u32,) -> Weight; + fn claim_redemption() -> Weight; + fn update_homa_params() -> Weight; + fn update_bump_era_params() -> Weight; + fn reset_ledgers(u: u32,) -> Weight; + fn reset_current_era() -> Weight; +} + +/// Weights for module_homa using the Acala node and recommended hardware. +pub struct AcalaWeight(PhantomData); +impl WeightInfo for AcalaWeight { + fn on_initialize() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn on_initialize_with_bump_era() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn mint() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn request_redeem() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn fast_match_redeems(u: u32,) -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn claim_redemption() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn update_homa_params() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn update_bump_era_params() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn reset_ledgers(u: u32,) -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn reset_current_era() -> Weight { + (13_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn on_initialize() -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn on_initialize_with_bump_era() -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn mint() -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn request_redeem() -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn fast_match_redeems(u: u32,) -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn claim_redemption() -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn update_homa_params() -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn update_bump_era_params() -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn reset_ledgers(u: u32,) -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn reset_current_era() -> Weight { + (13_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } +} diff --git a/runtime/karura/src/benchmarking/mod.rs b/runtime/karura/src/benchmarking/mod.rs index a15d94cfa8..0a2b06cbe6 100644 --- a/runtime/karura/src/benchmarking/mod.rs +++ b/runtime/karura/src/benchmarking/mod.rs @@ -51,6 +51,9 @@ pub mod evm { pub mod evm_accounts { include!("../../../mandala/src/benchmarking/evm_accounts.rs"); } +pub mod homa { + include!("../../../mandala/src/benchmarking/homa.rs"); +} pub mod honzon { include!("../../../mandala/src/benchmarking/honzon.rs"); } diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 81825ea564..0325d1d170 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -1594,8 +1594,6 @@ pub fn create_x2_parachain_multilocation(index: u16) -> MultiLocation { } parameter_types! { - pub const KSMCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); - pub const LKSMCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::LKSM); pub MinimumMintThreshold: Balance = 50 * cent(KSM); pub MinimumRedeemThreshold: Balance = 5 * dollar(LKSM); pub RelayChainSovereignSubAccount: MultiLocation = create_x2_parachain_multilocation(RelayChainSubAccountId::HomaLite as u16); @@ -1620,8 +1618,8 @@ impl module_homa_lite::Config for Runtime { type Event = Event; type WeightInfo = weights::module_homa_lite::WeightInfo; type Currency = Currencies; - type StakingCurrencyId = KSMCurrencyId; - type LiquidCurrencyId = LKSMCurrencyId; + type StakingCurrencyId = GetStakingCurrencyId; + type LiquidCurrencyId = GetLiquidCurrencyId; type GovernanceOrigin = EnsureRootOrHalfGeneralCouncil; type MinimumMintThreshold = MinimumMintThreshold; type MinimumRedeemThreshold = MinimumRedeemThreshold; @@ -1652,14 +1650,15 @@ impl module_homa::Config for Runtime { type Event = Event; type Currency = Currencies; type GovernanceOrigin = EnsureRootOrHalfGeneralCouncil; - type StakingCurrencyId = KSMCurrencyId; - type LiquidCurrencyId = LKSMCurrencyId; + type StakingCurrencyId = GetStakingCurrencyId; + type LiquidCurrencyId = GetLiquidCurrencyId; type PalletId = HomaPalletId; type TreasuryAccount = HomaTreasuryAccount; type DefaultExchangeRate = DefaultExchangeRate; type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; type BondingDuration = KusamaBondingDuration; type HomaXcm = HomaXcm; + type WeightInfo = (); } pub struct SubAccountIndexMultiLocationConvertor; @@ -1672,7 +1671,7 @@ impl Convert for SubAccountIndexMultiLocationConvertor { impl module_homa_xcm::Config for Runtime { type Event = Event; type UpdateOrigin = EnsureRootOrHalfGeneralCouncil; - type StakingCurrencyId = KSMCurrencyId; + type StakingCurrencyId = GetStakingCurrencyId; type ParachainAccount = ParachainAccount; type RelayChainUnbondingSlashingSpans = RelayChainUnbondingSlashingSpans; type SovereignSubAccountLocationConvert = SubAccountIndexMultiLocationConvertor; @@ -2241,6 +2240,7 @@ impl_runtime_apis! { orml_list_benchmark!(list, extra, module_cdp_engine, benchmarking::cdp_engine); orml_list_benchmark!(list, extra, module_emergency_shutdown, benchmarking::emergency_shutdown); orml_list_benchmark!(list, extra, module_evm, benchmarking::evm); + orml_list_benchmark!(list, extra, module_homa, benchmarking::homa); orml_list_benchmark!(list, extra, module_honzon, benchmarking::honzon); orml_list_benchmark!(list, extra, module_cdp_treasury, benchmarking::cdp_treasury); orml_list_benchmark!(list, extra, module_collator_selection, benchmarking::collator_selection); @@ -2300,6 +2300,7 @@ impl_runtime_apis! { orml_add_benchmark!(params, batches, module_cdp_engine, benchmarking::cdp_engine); orml_add_benchmark!(params, batches, module_emergency_shutdown, benchmarking::emergency_shutdown); orml_add_benchmark!(params, batches, module_evm, benchmarking::evm); + orml_add_benchmark!(params, batches, module_homa, benchmarking::homa); orml_add_benchmark!(params, batches, module_honzon, benchmarking::honzon); orml_add_benchmark!(params, batches, module_cdp_treasury, benchmarking::cdp_treasury); orml_add_benchmark!(params, batches, module_collator_selection, benchmarking::collator_selection); diff --git a/runtime/mandala/Cargo.toml b/runtime/mandala/Cargo.toml index 453a040779..ce7de9ae73 100644 --- a/runtime/mandala/Cargo.toml +++ b/runtime/mandala/Cargo.toml @@ -112,6 +112,7 @@ module-prices = { path = "../../modules/prices", default-features = false } module-incentives = { path = "../../modules/incentives", default-features = false } module-support = { path = "../../modules/support", default-features = false } module-homa = { path = "../../modules/homa", default-features = false } +module-homa-xcm = { path = "../../modules/homa-xcm", default-features = false } module-homa-lite = { path = "../../modules/homa-lite", default-features = false } module-nominees-election = { path = "../../modules/nominees-election", default-features = false } module-session-manager = { path = "../../modules/session-manager", default-features = false } @@ -243,6 +244,8 @@ std = [ "module-prices/std", "module-incentives/std", "module-support/std", + "module-homa/std", + "module-homa-xcm/std", "module-homa-lite/std", "module-nominees-election/std", "module-session-manager/std", @@ -348,6 +351,8 @@ try-runtime = [ "module-nft/try-runtime", "module-prices/try-runtime", "module-incentives/try-runtime", + "module-homa/try-runtime", + "module-homa-xcm/try-runtime", "module-homa-lite/try-runtime", "module-nominees-election/try-runtime", "module-session-manager/try-runtime", diff --git a/runtime/mandala/src/benchmarking/homa.rs b/runtime/mandala/src/benchmarking/homa.rs new file mode 100644 index 0000000000..1e3136b7d1 --- /dev/null +++ b/runtime/mandala/src/benchmarking/homa.rs @@ -0,0 +1,164 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2021 Acala Foundation. +// 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::{ + AccountId, ActiveSubAccountsIndexList, Balance, Currencies, GetLiquidCurrencyId, GetStakingCurrencyId, Homa, Rate, + Runtime, +}; + +use super::utils::set_balance; +use frame_benchmarking::{account, whitelisted_caller}; +use frame_support::traits::OnInitialize; +use frame_system::RawOrigin; +use module_homa::UnlockChunk; +use orml_benchmarking::runtime_benchmarks; +use orml_traits::MultiCurrency; +use sp_runtime::FixedPointNumber; +use sp_std::prelude::*; + +const SEED: u32 = 0; + +runtime_benchmarks! { + { Runtime, module_homa } + + on_initialize { + }: { + let _ = Homa::on_initialize(1); + } + + on_initialize_with_bump_era { + let minter: AccountId = account("minter", 0, SEED); + let redeemer: AccountId = account("redeemer", 0, SEED); + let sub_account_index = ActiveSubAccountsIndexList::get().first().unwrap().clone(); + + set_balance(GetStakingCurrencyId::get(), &minter, 1_000_000_000_000_000); + set_balance(GetLiquidCurrencyId::get(), &redeemer, 1_000_000_000_000_000 * 10); + Homa::reset_ledgers( + RawOrigin::Root.into(), + vec![(sub_account_index, Some(1_000_000_000_000_000), Some(vec![UnlockChunk{value: 1_000_000_000_000, era: 10}]))] + )?; + Homa::reset_current_era(RawOrigin::Root.into(), 9)?; + Homa::update_homa_params( + RawOrigin::Root.into(), + Some(10_000_000_000_000_000), + Some(Rate::saturating_from_rational(1, 100)), + None, + None, + Some(Rate::saturating_from_rational(20, 100)), + None, + )?; + Homa::update_bump_era_params(RawOrigin::Root.into(), None, Some(1))?; + + Homa::mint(RawOrigin::Signed(minter).into(), 100_000_000_000_000)?; + Homa::request_redeem(RawOrigin::Signed(redeemer).into(), 5_000_000_000_000_000, true)?; + }: { + let _ = Homa::on_initialize(1); + } + + mint { + let caller: AccountId = whitelisted_caller(); + let amount = 10_000_000_000_000; + + Homa::update_homa_params( + RawOrigin::Root.into(), + Some(amount * 10), + Some(Rate::saturating_from_rational(1, 10000)), + None, + None, + None, + None, + )?; + set_balance(GetStakingCurrencyId::get(), &caller, amount * 2); + }: _(RawOrigin::Signed(caller), amount) + + request_redeem { + let caller: AccountId = whitelisted_caller(); + let amount = 10_000_000_000_000; + + set_balance(GetLiquidCurrencyId::get(), &caller, amount * 2); + }: _(RawOrigin::Signed(caller), amount, true) + + fast_match_redeems { + let n in 1 .. 50; + let caller: AccountId = whitelisted_caller(); + let minter: AccountId = account("minter", 0, SEED); + let mint_amount = 1_000_000_000_000_000; + + set_balance(GetStakingCurrencyId::get(), &minter, mint_amount * 2); + Homa::update_homa_params( + RawOrigin::Root.into(), + Some(mint_amount * 10), + Some(Rate::saturating_from_rational(1, 10000)), + None, + None, + None, + None, + )?; + Homa::mint(RawOrigin::Signed(minter.clone()).into(), mint_amount)?; + + let mut redeem_request_list: Vec = vec![]; + let redeem_amount = 1_000_000_000_000; + for i in 0 .. n { + let redeemer = account("redeemer", i, SEED); + >::transfer(GetLiquidCurrencyId::get(), &minter, &redeemer, redeem_amount * 2)?; + Homa::request_redeem(RawOrigin::Signed(redeemer.clone()).into(), redeem_amount, true)?; + redeem_request_list.push(redeemer); + } + }: _(RawOrigin::Signed(caller), redeem_request_list) + + claim_redemption { + let caller: AccountId = whitelisted_caller(); + let redeemer: AccountId = account("redeemer", 0, SEED); + let redeption_amount = 1_000_000_000_000; + + module_homa::Unbondings::::insert(&redeemer, 1, redeption_amount); + set_balance(GetStakingCurrencyId::get(), &Homa::account_id(), redeption_amount); + module_homa::UnclaimedRedemption::::put(redeption_amount); + Homa::reset_current_era(RawOrigin::Root.into(), 1)?; + }: _(RawOrigin::Signed(caller), redeemer) + + update_homa_params {}: _( + RawOrigin::Root, + Some(1_000_000_000_000), + Some(Rate::saturating_from_rational(1, 100)), + Some(1_000_000_000_000), + Some(1_000_000_000_000), + Some(Rate::saturating_from_rational(1, 100)), + Some(Rate::saturating_from_rational(1, 100))) + + update_bump_era_params {}: _(RawOrigin::Root, Some(3000), Some(7200)) + + reset_ledgers { + let n in 0 .. 10; + let mut updates: Vec<(u16, Option, Option>)> = vec![]; + for i in 0..n { + updates.push((i.try_into().unwrap(), Some(1), Some(vec![UnlockChunk{value: 1, era: 1}]))) + } + }: _(RawOrigin::Root, updates) + + reset_current_era {}: _(RawOrigin::Root, 1) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::benchmarking::utils::tests::new_test_ext; + use orml_benchmarking::impl_benchmark_test_suite; + + impl_benchmark_test_suite!(new_test_ext(),); +} diff --git a/runtime/mandala/src/benchmarking/mod.rs b/runtime/mandala/src/benchmarking/mod.rs index 53c25834ef..1e8eff59a2 100644 --- a/runtime/mandala/src/benchmarking/mod.rs +++ b/runtime/mandala/src/benchmarking/mod.rs @@ -33,6 +33,7 @@ pub mod dex; pub mod emergency_shutdown; pub mod evm; pub mod evm_accounts; +pub mod homa; pub mod honzon; pub mod incentives; pub mod nominees_election; diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 5e068496d2..8f385aa3ce 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -167,6 +167,7 @@ parameter_types! { pub const DEXPalletId: PalletId = PalletId(*b"aca/dexm"); pub const CDPTreasuryPalletId: PalletId = PalletId(*b"aca/cdpt"); pub const HonzonTreasuryPalletId: PalletId = PalletId(*b"aca/hztr"); + pub const HomaPalletId: PalletId = PalletId(*b"aca/homa"); pub const HomaTreasuryPalletId: PalletId = PalletId(*b"aca/hmtr"); pub const IncentivesPalletId: PalletId = PalletId(*b"aca/inct"); pub const CollatorPotId: PalletId = PalletId(*b"aca/cpot"); @@ -1256,11 +1257,49 @@ impl module_homa_lite::Config for Runtime { type StakingUpdateFrequency = OneDay; } +parameter_types! { + pub HomaTreasuryAccount: AccountId = HomaTreasuryPalletId::get().into_account(); + pub ActiveSubAccountsIndexList: Vec = vec![RelayChainSubAccountId::HomaLite as u16]; + pub RelayChainBondingDuration: EraIndex = 28; +} + +impl module_homa::Config for Runtime { + type Event = Event; + type Currency = Currencies; + type GovernanceOrigin = EnsureRootOrHalfGeneralCouncil; + type StakingCurrencyId = GetStakingCurrencyId; + type LiquidCurrencyId = GetLiquidCurrencyId; + type PalletId = HomaPalletId; + type TreasuryAccount = HomaTreasuryAccount; + type DefaultExchangeRate = DefaultExchangeRate; + type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; + type BondingDuration = RelayChainBondingDuration; + type HomaXcm = HomaXcm; + type WeightInfo = (); +} + +pub struct SubAccountIndexMultiLocationConvertor; +impl Convert for SubAccountIndexMultiLocationConvertor { + fn convert(sub_account_index: u16) -> MultiLocation { + create_x2_parachain_multilocation(sub_account_index) + } +} + +impl module_homa_xcm::Config for Runtime { + type Event = Event; + type UpdateOrigin = EnsureRootOrHalfGeneralCouncil; + type StakingCurrencyId = GetStakingCurrencyId; + type ParachainAccount = ParachainAccount; + type RelayChainUnbondingSlashingSpans = RelayChainUnbondingSlashingSpans; + type SovereignSubAccountLocationConvert = SubAccountIndexMultiLocationConvertor; + type RelayChainCallBuilder = RelayChainCallBuilder; + type XcmTransfer = XTokens; +} + parameter_types! { pub MinCouncilBondThreshold: Balance = dollar(LDOT); pub const NominateesCount: u32 = 7; pub const MaxUnlockingChunks: u32 = 7; - pub const NomineesElectionBondingDuration: EraIndex = 7; } impl module_nominees_election::Config for Runtime { @@ -1269,7 +1308,7 @@ impl module_nominees_election::Config for Runtime { type NomineeId = AccountId; type PalletId = NomineesElectionId; type MinBondThreshold = MinCouncilBondThreshold; - type BondingDuration = NomineesElectionBondingDuration; + type BondingDuration = RelayChainBondingDuration; type NominateesCount = NominateesCount; type MaxUnlockingChunks = MaxUnlockingChunks; type NomineeFilter = runtime_common::DummyNomineeFilter; @@ -2033,6 +2072,8 @@ construct_runtime! { // Homa NomineesElection: module_nominees_election::{Pallet, Call, Storage, Event} = 131, HomaLite: module_homa_lite::{Pallet, Call, Storage, Event} = 135, + Homa: module_homa::{Pallet, Call, Storage, Event} = 136, + HomaXcm: module_homa_xcm::{Pallet, Call, Storage, Event} = 137, // Acala Other Incentives: module_incentives::{Pallet, Storage, Call, Event} = 140, @@ -2340,6 +2381,7 @@ impl_runtime_apis! { orml_list_benchmark!(list, extra, module_nominees_election, benchmarking::nominees_election); orml_list_benchmark!(list, extra, module_emergency_shutdown, benchmarking::emergency_shutdown); orml_list_benchmark!(list, extra, module_evm, benchmarking::evm); + orml_list_benchmark!(list, extra, module_homa, benchmarking::homa); orml_list_benchmark!(list, extra, module_honzon, benchmarking::honzon); orml_list_benchmark!(list, extra, module_cdp_treasury, benchmarking::cdp_treasury); orml_list_benchmark!(list, extra, module_transaction_pause, benchmarking::transaction_pause); @@ -2402,6 +2444,7 @@ impl_runtime_apis! { orml_add_benchmark!(params, batches, module_nominees_election, benchmarking::nominees_election); orml_add_benchmark!(params, batches, module_emergency_shutdown, benchmarking::emergency_shutdown); orml_add_benchmark!(params, batches, module_evm, benchmarking::evm); + orml_add_benchmark!(params, batches, module_homa, benchmarking::homa); orml_add_benchmark!(params, batches, module_honzon, benchmarking::honzon); orml_add_benchmark!(params, batches, module_cdp_treasury, benchmarking::cdp_treasury); orml_add_benchmark!(params, batches, module_transaction_pause, benchmarking::transaction_pause); From cee25dc453dbb0a031288f464a98d27c2b66bf38 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Wed, 8 Dec 2021 21:29:20 +0800 Subject: [PATCH 14/27] update --- modules/homa/src/weights.rs | 148 +++++++++++---------- runtime/karura/src/lib.rs | 2 +- runtime/karura/src/weights/mod.rs | 1 + runtime/karura/src/weights/module_homa.rs | 102 ++++++++++++++ runtime/mandala/src/lib.rs | 2 +- runtime/mandala/src/weights/mod.rs | 1 + runtime/mandala/src/weights/module_homa.rs | 102 ++++++++++++++ 7 files changed, 285 insertions(+), 73 deletions(-) create mode 100644 runtime/karura/src/weights/module_homa.rs create mode 100644 runtime/mandala/src/weights/module_homa.rs diff --git a/modules/homa/src/weights.rs b/modules/homa/src/weights.rs index 874c54e653..60426c5037 100644 --- a/modules/homa/src/weights.rs +++ b/modules/homa/src/weights.rs @@ -16,10 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Autogenerated weights for module_homa_lite +//! Autogenerated weights for module_homa //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-11-24, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("karura-dev"), DB CACHE: 128 // Executed Command: @@ -33,7 +33,7 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --template=./templates/module-weight-template.hbs +// --template=./templates/runtime-weight-template.hbs // --output=./modules/homa/src/weights.rs @@ -51,11 +51,11 @@ pub trait WeightInfo { fn on_initialize_with_bump_era() -> Weight; fn mint() -> Weight; fn request_redeem() -> Weight; - fn fast_match_redeems(u: u32,) -> Weight; + fn fast_match_redeems(n: u32,) -> Weight; fn claim_redemption() -> Weight; fn update_homa_params() -> Weight; fn update_bump_era_params() -> Weight; - fn reset_ledgers(u: u32,) -> Weight; + fn reset_ledgers(n: u32,) -> Weight; fn reset_current_era() -> Weight; } @@ -63,53 +63,56 @@ pub trait WeightInfo { pub struct AcalaWeight(PhantomData); impl WeightInfo for AcalaWeight { fn on_initialize() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (6_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) } fn on_initialize_with_bump_era() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (422_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(29 as Weight)) + .saturating_add(T::DbWeight::get().writes(15 as Weight)) } fn mint() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (137_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(11 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) } fn request_redeem() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn fast_match_redeems(u: u32,) -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (77_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn fast_match_redeems(n: u32, ) -> Weight { + (7_163_000 as Weight) + // Standard Error: 260_000 + .saturating_add((101_229_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) + .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn claim_redemption() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (111_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) } fn update_homa_params() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } + (66_000_000 as Weight) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) + } fn update_bump_era_params() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + (29_000_000 as Weight) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn reset_ledgers(u: u32,) -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + fn reset_ledgers(n: u32, ) -> Weight { + (2_268_000 as Weight) + // Standard Error: 245_000 + .saturating_add((19_990_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn reset_current_era() -> Weight { - (13_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) + (22_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } } @@ -117,53 +120,56 @@ impl WeightInfo for AcalaWeight { // For backwards compatibility and tests impl WeightInfo for () { fn on_initialize() -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (6_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) } fn on_initialize_with_bump_era() -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (422_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(29 as Weight)) + .saturating_add(RocksDbWeight::get().writes(15 as Weight)) } fn mint() -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (137_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(11 as Weight)) + .saturating_add(RocksDbWeight::get().writes(7 as Weight)) } fn request_redeem() -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn fast_match_redeems(u: u32,) -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (77_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } + fn fast_match_redeems(n: u32, ) -> Weight { + (7_163_000 as Weight) + // Standard Error: 260_000 + .saturating_add((101_229_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads(8 as Weight)) + .saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) + .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn claim_redemption() -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (111_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(10 as Weight)) + .saturating_add(RocksDbWeight::get().writes(7 as Weight)) } fn update_homa_params() -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } + (66_000_000 as Weight) + .saturating_add(RocksDbWeight::get().writes(6 as Weight)) + } fn update_bump_era_params() -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + (29_000_000 as Weight) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn reset_ledgers(u: u32,) -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + fn reset_ledgers(n: u32, ) -> Weight { + (2_268_000 as Weight) + // Standard Error: 245_000 + .saturating_add((19_990_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn reset_current_era() -> Weight { - (13_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + (22_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } } diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 0325d1d170..7ff4306646 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -1658,7 +1658,7 @@ impl module_homa::Config for Runtime { type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; type BondingDuration = KusamaBondingDuration; type HomaXcm = HomaXcm; - type WeightInfo = (); + type WeightInfo = weights::module_homa::WeightInfo; } pub struct SubAccountIndexMultiLocationConvertor; diff --git a/runtime/karura/src/weights/mod.rs b/runtime/karura/src/weights/mod.rs index aefbeb7f92..f87f3d631e 100644 --- a/runtime/karura/src/weights/mod.rs +++ b/runtime/karura/src/weights/mod.rs @@ -29,6 +29,7 @@ pub mod module_dex; pub mod module_emergency_shutdown; pub mod module_evm; pub mod module_evm_accounts; +pub mod module_homa; pub mod module_homa_lite; pub mod module_honzon; pub mod module_incentives; diff --git a/runtime/karura/src/weights/module_homa.rs b/runtime/karura/src/weights/module_homa.rs new file mode 100644 index 0000000000..50797441e7 --- /dev/null +++ b/runtime/karura/src/weights/module_homa.rs @@ -0,0 +1,102 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2021 Acala Foundation. +// 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 . + +//! Autogenerated weights for module_homa +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2021-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("karura-dev"), DB CACHE: 128 + +// Executed Command: +// target/release/acala +// benchmark +// --chain=karura-dev +// --steps=50 +// --repeat=20 +// --pallet=* +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=./templates/runtime-weight-template.hbs +// --output=./runtime/karura/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for module_homa. +pub struct WeightInfo(PhantomData); +impl module_homa::WeightInfo for WeightInfo { + fn on_initialize() -> Weight { + (6_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + } + fn on_initialize_with_bump_era() -> Weight { + (422_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(29 as Weight)) + .saturating_add(T::DbWeight::get().writes(15 as Weight)) + } + fn mint() -> Weight { + (137_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(11 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) + } + fn request_redeem() -> Weight { + (77_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn fast_match_redeems(n: u32, ) -> Weight { + (7_163_000 as Weight) + // Standard Error: 260_000 + .saturating_add((101_229_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) + .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) + } + fn claim_redemption() -> Weight { + (111_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) + } + fn update_homa_params() -> Weight { + (66_000_000 as Weight) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) + } + fn update_bump_era_params() -> Weight { + (29_000_000 as Weight) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn reset_ledgers(n: u32, ) -> Weight { + (2_268_000 as Weight) + // Standard Error: 245_000 + .saturating_add((19_990_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) + } + fn reset_current_era() -> Weight { + (22_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } +} diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 8f385aa3ce..bf010b846b 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -1275,7 +1275,7 @@ impl module_homa::Config for Runtime { type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; type BondingDuration = RelayChainBondingDuration; type HomaXcm = HomaXcm; - type WeightInfo = (); + type WeightInfo = weights::module_homa::WeightInfo; } pub struct SubAccountIndexMultiLocationConvertor; diff --git a/runtime/mandala/src/weights/mod.rs b/runtime/mandala/src/weights/mod.rs index b825f4fdcc..51e957343e 100644 --- a/runtime/mandala/src/weights/mod.rs +++ b/runtime/mandala/src/weights/mod.rs @@ -29,6 +29,7 @@ pub mod module_dex; pub mod module_emergency_shutdown; pub mod module_evm; pub mod module_evm_accounts; +pub mod module_homa; pub mod module_homa_lite; pub mod module_honzon; pub mod module_incentives; diff --git a/runtime/mandala/src/weights/module_homa.rs b/runtime/mandala/src/weights/module_homa.rs new file mode 100644 index 0000000000..9be92c675f --- /dev/null +++ b/runtime/mandala/src/weights/module_homa.rs @@ -0,0 +1,102 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2021 Acala Foundation. +// 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 . + +//! Autogenerated weights for module_homa +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2021-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("mandala-latest"), DB CACHE: 128 + +// Executed Command: +// target/release/acala +// benchmark +// --chain=mandala-latest +// --steps=50 +// --repeat=20 +// --pallet=* +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=./templates/runtime-weight-template.hbs +// --output=./runtime/mandala/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for module_homa. +pub struct WeightInfo(PhantomData); +impl module_homa::WeightInfo for WeightInfo { + fn on_initialize() -> Weight { + (6_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + } + fn on_initialize_with_bump_era() -> Weight { + (422_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(29 as Weight)) + .saturating_add(T::DbWeight::get().writes(15 as Weight)) + } + fn mint() -> Weight { + (137_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(11 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) + } + fn request_redeem() -> Weight { + (77_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn fast_match_redeems(n: u32, ) -> Weight { + (7_163_000 as Weight) + // Standard Error: 260_000 + .saturating_add((101_229_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) + .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) + } + fn claim_redemption() -> Weight { + (111_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) + } + fn update_homa_params() -> Weight { + (66_000_000 as Weight) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) + } + fn update_bump_era_params() -> Weight { + (29_000_000 as Weight) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn reset_ledgers(n: u32, ) -> Weight { + (2_268_000 as Weight) + // Standard Error: 245_000 + .saturating_add((19_990_000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) + } + fn reset_current_era() -> Weight { + (22_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } +} From 38f892705ca1e973b3e4ca70d56e49dddda1c6d1 Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Fri, 10 Dec 2021 15:59:55 +1300 Subject: [PATCH 15/27] Added an integration test for the full mint to redeem process (#1689) * Added an integration test for the full mint to redeem process * Corrected some typos Co-authored-by: Roy Yang --- modules/homa/src/lib.rs | 38 ++-- modules/homa/src/tests.rs | 10 +- runtime/integration-tests/src/homa_xcm.rs | 203 ++++++++++++++++++++++ runtime/integration-tests/src/setup.rs | 2 +- 4 files changed, 228 insertions(+), 25 deletions(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 326cfae2b8..097a979f8a 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -172,7 +172,7 @@ pub mod module { /// Redeem request is redeemed by unbond on relaychain. \[redeemer, /// era_index_when_unbond, liquid_amount, unbonding_staking_amount\] RedeemedByUnbond(T::AccountId, EraIndex, Balance, Balance), - /// The redeemer withdraw expired redemption. \[redeemer, redeption_amount\] + /// The redeemer withdraw expired redemption. \[redeemer, redemption_amount\] WithdrawRedemption(T::AccountId, Balance), /// The current era has been bumped. \[new_era_index\] CurrentEraBumped(EraIndex), @@ -234,8 +234,8 @@ pub mod module { /// The total amount of void liquid currency. It's will not be issued, /// used to avoid newly issued LDOT to obtain the incoming staking income from relaychain. /// And it is guaranteed that the current exchange rate between liquid currency and staking - /// currency will not change. It will be reset to 0 at the beginning of the rebalance when new - /// era. + /// currency will not change. It will be reset to 0 at the beginning of the `rebalance` when new + /// era starts. /// /// TotalVoidLiquid value: LiquidCurrencyAmount #[pallet::storage] @@ -258,7 +258,7 @@ pub mod module { /// The records of unbonding by AccountId. /// - /// Unbondings: double_map AccountId, ExpireEraIndex => UnboundingStakingCurrencyAmount + /// Unbondings: double_map AccountId, ExpireEraIndex => UnbondingStakingCurrencyAmount #[pallet::storage] #[pallet::getter(fn unbondings)] pub type Unbondings = @@ -293,7 +293,7 @@ pub mod module { #[pallet::getter(fn redeem_threshold)] pub type RedeemThreshold = StorageValue<_, Balance, ValueQuery>; - /// The rate of Homa drawn from the staking reward as commision. + /// The rate of Homa drawn from the staking reward as commission. /// The draw will be transfer to TreasuryAccount of Homa in liquid currency. /// /// CommissionRate: value: Rate @@ -506,7 +506,7 @@ pub mod module { /// Parameters: /// - `soft_bonded_cap_per_sub_account`: soft cap of staking amount for a single nominator /// on relaychain to obtain the best staking rewards. - /// - `estimated_reward_rate_per_era`: the esstaking yield of each era on the current relay + /// - `estimated_reward_rate_per_era`: the estimated staking yield of each era on the current relay /// chain. /// - `mint_threshold`: the staking currency amount of threshold when mint. /// - `redeem_threshold`: the liquid currency amount of threshold when request redeem. @@ -732,11 +732,11 @@ pub mod module { .saturating_mul_int(liquid_currency_limit); let module_account = Self::account_id(); - // calculate the acutal liquid currency to be used to redeem + // calculate the actual liquid currency to be used to redeem let actual_liquid_to_redeem = if liquid_limit_at_fee_rate >= request_amount { request_amount } else { - // if cannot fast match the request amount fully, at least keep RedeemThreshold as remainer. + // if cannot fast match the request amount fully, at least keep RedeemThreshold as remainder. liquid_limit_at_fee_rate.min(request_amount.saturating_sub(Self::redeem_threshold())) }; @@ -769,9 +769,9 @@ pub mod module { } // update request amount - let remainer_request_amount = request_amount.saturating_sub(actual_liquid_to_redeem); - if !remainer_request_amount.is_zero() { - *maybe_request = Some((remainer_request_amount, allow_fast_match)); + let remainder_request_amount = request_amount.saturating_sub(actual_liquid_to_redeem); + if !remainder_request_amount.is_zero() { + *maybe_request = Some((remainder_request_amount, allow_fast_match)); } } @@ -779,7 +779,7 @@ pub mod module { }) } - /// Accumulate staking rewards according to EstimatedRewardRatePerEra and era interal. + /// Accumulate staking rewards according to EstimatedRewardRatePerEra and era internally. /// And draw commission from estimated staking rewards by issuing liquid currency to /// TreasuryAccount. Note: This will cause some losses to the minters in previous_era, /// because they have been already deducted some liquid currency amount when mint in @@ -843,7 +843,7 @@ pub mod module { if !expired_unlocking.is_zero() { T::HomaXcm::withdraw_unbonded_from_sub_account(sub_account_index, expired_unlocking)?; - // udpate ledger + // update ledger Self::do_update_ledger(sub_account_index, |before| -> DispatchResult { *before = new_ledger; Ok(()) @@ -876,7 +876,7 @@ pub mod module { .iter() .map(|index| (*index, Self::staking_ledgers(index).unwrap_or_default().bonded)) .collect(); - let (distribution, remainer) = distribute_increment::( + let (distribution, remainder) = distribute_increment::( bonded_list, to_bond_pool, Some(Self::soft_bonded_cap_per_sub_account().saturating_add(xcm_transfer_fee)), @@ -891,7 +891,7 @@ pub mod module { let bond_amount = amount.saturating_sub(xcm_transfer_fee); T::HomaXcm::bond_extra_on_sub_account(sub_account_index, bond_amount)?; - // udpate ledger + // update ledger Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { ledger.bonded = ledger.bonded.saturating_add(bond_amount); Ok(()) @@ -900,7 +900,7 @@ pub mod module { } // update pool - ToBondPool::::mutate(|pool| *pool = remainer); + ToBondPool::::mutate(|pool| *pool = remainder); } Ok(()) @@ -949,7 +949,7 @@ pub mod module { if !unbond_amount.is_zero() { T::HomaXcm::unbond_on_sub_account(sub_account_index, unbond_amount)?; - // udpate ledger + // update ledger Self::do_update_ledger(sub_account_index, |ledger| -> DispatchResult { ledger.bonded = ledger.bonded.saturating_sub(unbond_amount); ledger.unlocking.push(UnlockChunk { @@ -1047,7 +1047,7 @@ pub fn distribute_increment( pub fn distribute_decrement( mut amount_list: Vec<(Index, Balance)>, total_decrement: Balance, - amount_remainer: Option, + amount_remainder: Option, minimum_decrement: Option, ) -> (Vec<(Index, Balance)>, Balance) { let mut remain_decrement = total_decrement; @@ -1062,7 +1062,7 @@ pub fn distribute_decrement( } let decrement_distribution = amount - .saturating_sub(amount_remainer.unwrap_or_else(Bounded::min_value)) + .saturating_sub(amount_remainder.unwrap_or_else(Bounded::min_value)) .min(remain_decrement); if decrement_distribution.is_zero() || decrement_distribution < minimum_decrement.unwrap_or_else(Bounded::min_value) diff --git a/modules/homa/src/tests.rs b/modules/homa/src/tests.rs index a9ca4bda13..1e6da1d2eb 100644 --- a/modules/homa/src/tests.rs +++ b/modules/homa/src/tests.rs @@ -200,7 +200,7 @@ fn claim_redemption_works() { assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &ALICE), 0); assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); - // no available expired redemption, nothing happend. + // no available expired redemption, nothing happened. assert_ok!(Homa::claim_redemption(Origin::signed(BOB), ALICE)); assert_eq!(Homa::unbondings(&ALICE, 1), 1_000_000); assert_eq!(Homa::unbondings(&ALICE, 2), 2_000_000); @@ -209,7 +209,7 @@ fn claim_redemption_works() { assert_eq!(Homa::unclaimed_redemption(), 0); assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &Homa::account_id()), 0); - // there is available expired redemption, but UnclaimedRedemption is not enought. + // there is available expired redemption, but UnclaimedRedemption is not enough. RelayChainCurrentEra::::put(2); assert_noop!( Homa::claim_redemption(Origin::signed(BOB), ALICE), @@ -630,7 +630,7 @@ fn do_fast_match_redeem_works() { ); // Bob's redeem request is able to be fast matched partially, - // because must remain `RedeemThreshold` even if `ToBondPool` is enought. + // because must remain `RedeemThreshold` even if `ToBondPool` is enough. assert_ok!(Homa::do_fast_match_redeem(&BOB)); System::assert_last_event(Event::Homa(crate::Event::RedeemedByFastMatch( BOB, 5_500_000, 550_000, 500_499, @@ -1026,7 +1026,7 @@ fn process_redeem_requests_works() { }) ); - // total_bonded is enought to process all redeem requests + // total_bonded is enough to process all redeem requests assert_ok!(Homa::process_redeem_requests(1)); System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( ALICE, 1, 20_000_000, 2_000_000, @@ -1068,7 +1068,7 @@ fn process_redeem_requests_works() { 40_000_000 ); - // total_bonded is not enought to process all redeem requests + // total_bonded is not enough to process all redeem requests assert_ok!(Homa::process_redeem_requests(2)); System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( BOB, 2, 20_000_000, 2_000_000, diff --git a/runtime/integration-tests/src/homa_xcm.rs b/runtime/integration-tests/src/homa_xcm.rs index 1110823c10..854e90a5a5 100644 --- a/runtime/integration-tests/src/homa_xcm.rs +++ b/runtime/integration-tests/src/homa_xcm.rs @@ -414,3 +414,206 @@ fn homa_xcm_unbond_on_sub_account_works() { ); }); } + +// Test the entire process from Mint to Redeem. +#[test] +fn homa_mint_and_redeem_works() { + let homa_lite_sub_account: AccountId = + hex_literal::hex!["d7b8926b326dd349355a9a7cca6606c1e0eb6fd2b506066b518c7155ff0d8297"].into(); + let mut parachain_account: AccountId = AccountId::default(); + Karura::execute_with(|| { + parachain_account = ParachainAccount::get(); + }); + KusamaNet::execute_with(|| { + // Transfer some KSM into the parachain. + assert_ok!(kusama_runtime::XcmPallet::reserve_transfer_assets( + kusama_runtime::Origin::signed(ALICE.into()), + Box::new(Parachain(2000).into().into()), + Box::new( + Junction::AccountId32 { + id: alice().into(), + network: NetworkId::Any + } + .into() + .into() + ), + Box::new((Here, 2001 * dollar(RELAY_CHAIN_CURRENCY)).into()), + 0 + )); + + // Transfer some KSM into the parachain. + assert_ok!(kusama_runtime::Balances::transfer( + kusama_runtime::Origin::signed(ALICE.into()), + MultiAddress::Id(homa_lite_sub_account.clone()), + dollar(RELAY_CHAIN_CURRENCY) + )); + + assert_ok!(kusama_runtime::Staking::bond( + kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), + MultiAddress::Id(homa_lite_sub_account.clone()), + dollar(RELAY_CHAIN_CURRENCY), + pallet_staking::RewardDestination::::Staked, + )); + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account), + 2003 * dollar(RELAY_CHAIN_CURRENCY) + ); + assert_eq!( + kusama_runtime::Balances::free_balance(&homa_lite_sub_account), + dollar(RELAY_CHAIN_CURRENCY), + ); + }); + + Karura::execute_with(|| { + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(alice())), + RELAY_CHAIN_CURRENCY, + 1_000 * dollar(RELAY_CHAIN_CURRENCY), + 0 + )); + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(bob())), + RELAY_CHAIN_CURRENCY, + 1_000 * dollar(RELAY_CHAIN_CURRENCY), + 0 + )); + + configure_homa_and_homa_xcm(); + + // Test mint works + // Amount bonded = $1000 - XCM_FEE = 999_990_000_000_000 + assert_ok!(Homa::mint(Origin::signed(alice()), 1_000 * dollar(RELAY_CHAIN_CURRENCY))); + assert_ok!(Homa::mint(Origin::signed(bob()), 1_000 * dollar(RELAY_CHAIN_CURRENCY))); + + assert_eq!(Homa::get_total_bonded(), 0); + assert_eq!(Homa::get_total_staking_currency(), 2_000 * dollar(RELAY_CHAIN_CURRENCY)); + + // Synchronize with Relay chain via Xcm messages. Also update internal storage. + assert_ok!(Homa::bump_current_era()); + + assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(alice())), 10_000 * dollar(LIQUID_CURRENCY)); + assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(bob())), 10_000 * dollar(LIQUID_CURRENCY)); + assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), 0); + assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), 0); + + assert_eq!(Homa::get_total_bonded(), 2_000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); + assert_eq!(Homa::get_total_staking_currency(), 2_000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); + }); + + KusamaNet::execute_with(|| { + // Ensure the correct amount is bonded. + let ledger = kusama_runtime::Staking::ledger(&homa_lite_sub_account).expect("record should exist"); + assert_eq!(ledger.total, 2001 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); + assert_eq!(ledger.active, 2001 * dollar(RELAY_CHAIN_CURRENCY)- XCM_FEE); + + // 2 x XCM fee is paid: for Mint and Redeem + assert_eq!( + kusama_runtime::Balances::free_balance(¶chain_account), + 3 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + ); + }); + + Karura::execute_with(|| { + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(alice())), + RELAY_CHAIN_CURRENCY, + 0, + 0 + )); + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(bob())), + RELAY_CHAIN_CURRENCY, + 0, + 0 + )); + + // Redeem the liquid currency. + assert_ok!(Homa::request_redeem( + Origin::signed(alice()), + 10_000 * dollar(LIQUID_CURRENCY), + false, + )); + assert_ok!(Homa::request_redeem( + Origin::signed(bob()), + 10_000 * dollar(LIQUID_CURRENCY), + false, + )); + + // Unbonds the tokens on the Relay chain. + assert_ok!(Homa::bump_current_era()); + let unbonding_era = Homa::relay_chain_current_era() + KusamaBondingDuration::get(); + assert_eq!(unbonding_era, 9); + + assert_eq!(Homa::unbondings(&alice(), unbonding_era), 999_995_000_000_000); + assert_eq!(Homa::unbondings(&bob(), unbonding_era), 999_995_000_000_000); + + assert_eq!(Homa::get_total_bonded(), 0); + assert_eq!(Homa::get_total_staking_currency(), 0); + assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), 0); + assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), 0); + }); + + KusamaNet::execute_with(|| { + // Some bonds are being unlocked via Xcm from the parachain. + let ledger = kusama_runtime::Staking::ledger(&homa_lite_sub_account).expect("record should exist"); + assert_eq!(ledger.total, 2001 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); + assert_eq!(ledger.active, dollar(RELAY_CHAIN_CURRENCY)); + + // Fast forward the era until unlocking period ends. + kusama_runtime::System::set_block_number(101_000); + for _i in 0..29 { + kusama_runtime::Staking::trigger_new_era(0, vec![]); + } + }); + + Karura::execute_with(|| { + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(alice())), + RELAY_CHAIN_CURRENCY, + 0, + 0 + )); + assert_ok!(Tokens::set_balance( + Origin::root(), + MultiAddress::Id(AccountId::from(bob())), + RELAY_CHAIN_CURRENCY, + 0, + 0 + )); + + // Wait for the chunk to unlock + for _ in 0..KusamaBondingDuration::get() + 1 { + assert_ok!(Homa::bump_current_era()); + } + + // Claim the unlocked chunk + assert_ok!(Homa::claim_redemption( + Origin::signed(alice()), + alice(), + )); + assert_ok!(Homa::claim_redemption( + Origin::signed(alice()), + bob(), + )); + + // Redeem process is completed. + assert_eq!(Homa::get_total_bonded(), 0); + assert_eq!(Homa::get_total_staking_currency(), 0); + assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), 999_995_000_000_000); + assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), 999_995_000_000_000); + assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(alice())), 0); + assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(bob())), 0); + }); + + KusamaNet::execute_with(|| { + // Unbonded chunks are withdrew. + let ledger = kusama_runtime::Staking::ledger(&homa_lite_sub_account).expect("record should exist"); + assert_eq!(ledger.total, dollar(RELAY_CHAIN_CURRENCY)); + assert_eq!(ledger.active, dollar(RELAY_CHAIN_CURRENCY)); + }); +} diff --git a/runtime/integration-tests/src/setup.rs b/runtime/integration-tests/src/setup.rs index 4259d3be90..76c6f01378 100644 --- a/runtime/integration-tests/src/setup.rs +++ b/runtime/integration-tests/src/setup.rs @@ -82,7 +82,7 @@ mod karura_imports { ParachainAccount, ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, SevenDays, System, Timestamp, TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, - NFT, + NFT, KusamaBondingDuration, }; pub use primitives::TradingPair; pub use runtime_common::{cent, dollar, millicent, KAR, KSM, KUSD, LKSM}; From 9f42108926cb5d32831d75eaefb186b08a5de942 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Fri, 10 Dec 2021 17:51:35 +0800 Subject: [PATCH 16/27] config correct KsmBondingDuration --- runtime/integration-tests/src/homa_xcm.rs | 54 ++++++++++++++--------- runtime/karura/src/lib.rs | 2 +- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/runtime/integration-tests/src/homa_xcm.rs b/runtime/integration-tests/src/homa_xcm.rs index 854e90a5a5..4848b95feb 100644 --- a/runtime/integration-tests/src/homa_xcm.rs +++ b/runtime/integration-tests/src/homa_xcm.rs @@ -447,7 +447,7 @@ fn homa_mint_and_redeem_works() { MultiAddress::Id(homa_lite_sub_account.clone()), dollar(RELAY_CHAIN_CURRENCY) )); - + assert_ok!(kusama_runtime::Staking::bond( kusama_runtime::Origin::signed(homa_lite_sub_account.clone()), MultiAddress::Id(homa_lite_sub_account.clone()), @@ -484,29 +484,41 @@ fn homa_mint_and_redeem_works() { // Test mint works // Amount bonded = $1000 - XCM_FEE = 999_990_000_000_000 - assert_ok!(Homa::mint(Origin::signed(alice()), 1_000 * dollar(RELAY_CHAIN_CURRENCY))); + assert_ok!(Homa::mint( + Origin::signed(alice()), + 1_000 * dollar(RELAY_CHAIN_CURRENCY) + )); assert_ok!(Homa::mint(Origin::signed(bob()), 1_000 * dollar(RELAY_CHAIN_CURRENCY))); assert_eq!(Homa::get_total_bonded(), 0); assert_eq!(Homa::get_total_staking_currency(), 2_000 * dollar(RELAY_CHAIN_CURRENCY)); - + // Synchronize with Relay chain via Xcm messages. Also update internal storage. assert_ok!(Homa::bump_current_era()); - assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(alice())), 10_000 * dollar(LIQUID_CURRENCY)); - assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(bob())), 10_000 * dollar(LIQUID_CURRENCY)); + assert_eq!( + Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(alice())), + 10_000 * dollar(LIQUID_CURRENCY) + ); + assert_eq!( + Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(bob())), + 10_000 * dollar(LIQUID_CURRENCY) + ); assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), 0); assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), 0); - + assert_eq!(Homa::get_total_bonded(), 2_000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); - assert_eq!(Homa::get_total_staking_currency(), 2_000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); + assert_eq!( + Homa::get_total_staking_currency(), + 2_000 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE + ); }); KusamaNet::execute_with(|| { // Ensure the correct amount is bonded. let ledger = kusama_runtime::Staking::ledger(&homa_lite_sub_account).expect("record should exist"); assert_eq!(ledger.total, 2001 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); - assert_eq!(ledger.active, 2001 * dollar(RELAY_CHAIN_CURRENCY)- XCM_FEE); + assert_eq!(ledger.active, 2001 * dollar(RELAY_CHAIN_CURRENCY) - XCM_FEE); // 2 x XCM fee is paid: for Mint and Redeem assert_eq!( @@ -530,7 +542,7 @@ fn homa_mint_and_redeem_works() { 0, 0 )); - + // Redeem the liquid currency. assert_ok!(Homa::request_redeem( Origin::signed(alice()), @@ -546,7 +558,7 @@ fn homa_mint_and_redeem_works() { // Unbonds the tokens on the Relay chain. assert_ok!(Homa::bump_current_era()); let unbonding_era = Homa::relay_chain_current_era() + KusamaBondingDuration::get(); - assert_eq!(unbonding_era, 9); + assert_eq!(unbonding_era, 30); assert_eq!(Homa::unbondings(&alice(), unbonding_era), 999_995_000_000_000); assert_eq!(Homa::unbondings(&bob(), unbonding_era), 999_995_000_000_000); @@ -592,20 +604,20 @@ fn homa_mint_and_redeem_works() { } // Claim the unlocked chunk - assert_ok!(Homa::claim_redemption( - Origin::signed(alice()), - alice(), - )); - assert_ok!(Homa::claim_redemption( - Origin::signed(alice()), - bob(), - )); - + assert_ok!(Homa::claim_redemption(Origin::signed(alice()), alice(),)); + assert_ok!(Homa::claim_redemption(Origin::signed(alice()), bob(),)); + // Redeem process is completed. assert_eq!(Homa::get_total_bonded(), 0); assert_eq!(Homa::get_total_staking_currency(), 0); - assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), 999_995_000_000_000); - assert_eq!(Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), 999_995_000_000_000); + assert_eq!( + Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(alice())), + 999_995_000_000_000 + ); + assert_eq!( + Tokens::free_balance(RELAY_CHAIN_CURRENCY, &AccountId::from(bob())), + 999_995_000_000_000 + ); assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(alice())), 0); assert_eq!(Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(bob())), 0); }); diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 24f06f4cc6..a16781815a 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -1643,7 +1643,7 @@ impl module_homa_lite::Config for Runtime { parameter_types! { pub HomaTreasuryAccount: AccountId = HomaTreasuryPalletId::get().into_account(); pub ActiveSubAccountsIndexList: Vec = vec![RelayChainSubAccountId::HomaLite as u16]; - pub KusamaBondingDuration: EraIndex = 7; + pub KusamaBondingDuration: EraIndex = 28; } impl module_homa::Config for Runtime { From 6dc856395d4758ef1b60125642ce17d7c8a95756 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Fri, 10 Dec 2021 18:32:05 +0800 Subject: [PATCH 17/27] config LiquidStakingExchangeRateProvider from HomaLite to Homa --- modules/homa/src/lib.rs | 21 ++++++++++++--------- modules/homa/src/tests.rs | 14 ++++++++++++++ runtime/integration-tests/src/prices.rs | 8 ++++---- runtime/integration-tests/src/setup.rs | 14 +++++++------- runtime/karura/src/lib.rs | 3 +-- runtime/mandala/src/lib.rs | 2 +- 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 097a979f8a..7cf57bfb93 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -506,8 +506,8 @@ pub mod module { /// Parameters: /// - `soft_bonded_cap_per_sub_account`: soft cap of staking amount for a single nominator /// on relaychain to obtain the best staking rewards. - /// - `estimated_reward_rate_per_era`: the estimated staking yield of each era on the current relay - /// chain. + /// - `estimated_reward_rate_per_era`: the estimated staking yield of each era on the + /// current relay chain. /// - `mint_threshold`: the staking currency amount of threshold when mint. /// - `redeem_threshold`: the liquid currency amount of threshold when request redeem. /// - `commission_rate`: the rate to draw from estimated staking rewards as commission to @@ -681,14 +681,18 @@ pub mod module { Self::get_total_bonded().saturating_add(Self::to_bond_pool()) } + /// Calculate the total amount of liquid currency. + /// total_liquid_amount = total issuance of LiquidCurrencyId + TotalVoidLiquid + pub fn get_total_liquid_currency() -> Balance { + T::Currency::total_issuance(T::LiquidCurrencyId::get()).saturating_add(Self::total_void_liquid()) + } + /// Calculate the current exchange rate between the staking currency and liquid currency. - /// Note: ExchangeRate(staking : liquid) = total_staking_amount / (liquid_total_issuance + - /// total_void_liquid) If the exchange rate cannot be calculated, T::DefaultExchangeRate is - /// used. + /// Note: ExchangeRate(staking : liquid) = total_staking_amount / total_liquid_amount. + /// If the exchange rate cannot be calculated, T::DefaultExchangeRate is used. pub fn current_exchange_rate() -> ExchangeRate { let total_staking = Self::get_total_staking_currency(); - let total_liquid = - T::Currency::total_issuance(T::LiquidCurrencyId::get()).saturating_add(Self::total_void_liquid()); + let total_liquid = Self::get_total_liquid_currency(); if total_staking.is_zero() { T::DefaultExchangeRate::get() } else { @@ -820,8 +824,7 @@ pub mod module { let inflate_rate = commission_ratio .checked_div(&Ratio::one().saturating_sub(commission_ratio)) .unwrap_or_else(Ratio::max_value); - let inflate_liquid_amount = - inflate_rate.saturating_mul_int(T::Currency::total_issuance(liquid_currency_id)); + let inflate_liquid_amount = inflate_rate.saturating_mul_int(Self::get_total_liquid_currency()); T::Currency::deposit(liquid_currency_id, &T::TreasuryAccount::get(), inflate_liquid_amount)?; } diff --git a/modules/homa/src/tests.rs b/modules/homa/src/tests.rs index 1e6da1d2eb..57c4ade727 100644 --- a/modules/homa/src/tests.rs +++ b/modules/homa/src/tests.rs @@ -455,6 +455,20 @@ fn get_total_staking_currency_works() { }); } +#[test] +fn get_total_liquid_currency_works() { + ExtBuilder::default() + .balances(vec![(ALICE, LIQUID_CURRENCY_ID, 20_000_000)]) + .build() + .execute_with(|| { + assert_eq!(Currencies::total_issuance(LiquidCurrencyId::get()), 20_000_000); + assert_eq!(Homa::get_total_liquid_currency(), 20_000_000); + TotalVoidLiquid::::put(10_000_000); + assert_eq!(Currencies::total_issuance(LiquidCurrencyId::get()), 20_000_000); + assert_eq!(Homa::get_total_liquid_currency(), 30_000_000); + }); +} + #[test] fn current_exchange_rate_works() { ExtBuilder::default().build().execute_with(|| { diff --git a/runtime/integration-tests/src/prices.rs b/runtime/integration-tests/src/prices.rs index 95297c3739..965b2feb25 100644 --- a/runtime/integration-tests/src/prices.rs +++ b/runtime/integration-tests/src/prices.rs @@ -90,9 +90,9 @@ fn test_update_liquid_currency_price() { set_oracle_price(vec![(RELAY_CHAIN_CURRENCY, relaychain_price)]); - assert_ok!(HomaLite::set_total_staking_currency( + assert_ok!(Homa::reset_ledgers( Origin::root(), - 100 * dollar(RELAY_CHAIN_CURRENCY) + vec![(0, Some(100 * dollar(RELAY_CHAIN_CURRENCY)), None)] )); assert_eq!( @@ -100,9 +100,9 @@ fn test_update_liquid_currency_price() { Some(Ratio::saturating_from_rational(100, 1000)) ); - assert_ok!(HomaLite::set_total_staking_currency( + assert_ok!(Homa::reset_ledgers( Origin::root(), - 110 * dollar(RELAY_CHAIN_CURRENCY) + vec![(0, Some(110 * dollar(RELAY_CHAIN_CURRENCY)), None)] )); assert_eq!( diff --git a/runtime/integration-tests/src/setup.rs b/runtime/integration-tests/src/setup.rs index 76c6f01378..8bb0ec923a 100644 --- a/runtime/integration-tests/src/setup.rs +++ b/runtime/integration-tests/src/setup.rs @@ -48,7 +48,7 @@ mod mandala_imports { AuthoritysOriginId, Authorship, Balance, Balances, BlockNumber, Call, CdpEngine, CdpTreasury, CollatorSelection, CreateClassDeposit, CreateTokenDeposit, Currencies, CurrencyId, CurrencyIdConvert, DataDepositPerByte, DealWithFees, DefaultExchangeRate, Dex, EmergencyShutdown, EnabledTradingPairs, Event, - EvmAccounts, ExistentialDeposits, Get, GetNativeCurrencyId, HomaLite, Honzon, IdleScheduler, Loans, + EvmAccounts, ExistentialDeposits, Get, GetNativeCurrencyId, Homa, HomaLite, Honzon, IdleScheduler, Loans, MinRewardDistributeAmount, MinimumDebitValue, MultiLocation, NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, PalletCurrency, ParachainInfo, ParachainSystem, Proxy, ProxyType, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionKeys, SessionManager, SevenDays, System, @@ -77,12 +77,12 @@ mod karura_imports { AuctionManager, Authority, AuthoritysOriginId, Balance, Balances, BlockNumber, Call, CdpEngine, CdpTreasury, CreateClassDeposit, CreateTokenDeposit, Currencies, CurrencyId, CurrencyIdConvert, DataDepositPerByte, DefaultExchangeRate, Dex, EmergencyShutdown, Event, EvmAccounts, ExistentialDeposits, Get, GetNativeCurrencyId, - Homa, HomaLite, HomaXcm, Honzon, IdleScheduler, KaruraFoundationAccounts, Loans, MinimumDebitValue, - MultiLocation, NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, - ParachainAccount, ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, - RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, SevenDays, System, Timestamp, - TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, - NFT, KusamaBondingDuration, + Homa, HomaLite, HomaXcm, Honzon, IdleScheduler, KaruraFoundationAccounts, KusamaBondingDuration, Loans, + MinimumDebitValue, MultiLocation, NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, + OriginCaller, ParachainAccount, ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, + RelayChainBlockNumberProvider, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, + SevenDays, System, Timestamp, TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, + XcmExecutor, XcmUnbondFee, EVM, NFT, }; pub use primitives::TradingPair; pub use runtime_common::{cent, dollar, millicent, KAR, KSM, KUSD, LKSM}; diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index a16781815a..1eeb7688ab 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -823,8 +823,7 @@ impl module_prices::Config for Runtime { type GetStakingCurrencyId = GetStakingCurrencyId; type GetLiquidCurrencyId = GetLiquidCurrencyId; type LockOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - // TODO: need to switch to Homa after open Homa and close HomaLite - type LiquidStakingExchangeRateProvider = HomaLite; + type LiquidStakingExchangeRateProvider = Homa; type DEX = Dex; type Currency = Currencies; type Erc20InfoMapping = EvmErc20InfoMapping; diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 65925fcf32..411290f2f6 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -840,7 +840,7 @@ impl module_prices::Config for Runtime { type GetStakingCurrencyId = GetStakingCurrencyId; type GetLiquidCurrencyId = GetLiquidCurrencyId; type LockOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type LiquidStakingExchangeRateProvider = HomaLite; + type LiquidStakingExchangeRateProvider = Homa; type DEX = Dex; type Currency = Currencies; type Erc20InfoMapping = EvmErc20InfoMapping; From be41204877ddb5326d084de21461e8cb637b1339 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Mon, 13 Dec 2021 10:39:54 +0800 Subject: [PATCH 18/27] fix integration tests --- runtime/integration-tests/src/prices.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/runtime/integration-tests/src/prices.rs b/runtime/integration-tests/src/prices.rs index 965b2feb25..c4d7fb0d63 100644 --- a/runtime/integration-tests/src/prices.rs +++ b/runtime/integration-tests/src/prices.rs @@ -90,20 +90,32 @@ fn test_update_liquid_currency_price() { set_oracle_price(vec![(RELAY_CHAIN_CURRENCY, relaychain_price)]); + #[cfg(any(feature = "with-mandala-runtime", feature = "with-karura-runtime"))] assert_ok!(Homa::reset_ledgers( Origin::root(), vec![(0, Some(100 * dollar(RELAY_CHAIN_CURRENCY)), None)] )); + #[cfg(feature = "with-acala-runtime")] + assert_ok!(HomaLite::set_total_staking_currency( + Origin::root(), + 100 * dollar(RELAY_CHAIN_CURRENCY) + )); assert_eq!( RealTimePriceProvider::::get_relative_price(LIQUID_CURRENCY, RELAY_CHAIN_CURRENCY), Some(Ratio::saturating_from_rational(100, 1000)) ); + #[cfg(any(feature = "with-mandala-runtime", feature = "with-karura-runtime"))] assert_ok!(Homa::reset_ledgers( Origin::root(), vec![(0, Some(110 * dollar(RELAY_CHAIN_CURRENCY)), None)] )); + #[cfg(feature = "with-acala-runtime")] + assert_ok!(HomaLite::set_total_staking_currency( + Origin::root(), + 110 * dollar(RELAY_CHAIN_CURRENCY) + )); assert_eq!( RealTimePriceProvider::::get_relative_price(LIQUID_CURRENCY, RELAY_CHAIN_CURRENCY), From 107351cff516856a7c4e0214a21da56eed3d22f7 Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Tue, 14 Dec 2021 10:43:23 +1300 Subject: [PATCH 19/27] Re-adjusted the weight and fee in integration tests --- runtime/integration-tests/src/homa_xcm.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/runtime/integration-tests/src/homa_xcm.rs b/runtime/integration-tests/src/homa_xcm.rs index 4848b95feb..593610e851 100644 --- a/runtime/integration-tests/src/homa_xcm.rs +++ b/runtime/integration-tests/src/homa_xcm.rs @@ -28,8 +28,9 @@ use sp_runtime::MultiAddress; use xcm_emulator::TestExt; // Weight and fee cost is related to the XCM_WEIGHT passed in. -const XCM_WEIGHT: Weight = 10_000_000_000; +const XCM_WEIGHT: Weight = 20_000_000_000; const XCM_FEE: Balance = 10_000_000_000; +const ACTUAL_XCM_FEE: Balance = 639_999_960; fn get_xcm_weight() -> Vec<(HomaXcmOperation, Option, Option)> { vec![ @@ -141,7 +142,7 @@ fn homa_xcm_transfer_staking_to_sub_account_works() { // XCM fee is paid by the parachain account. assert_eq!( kusama_runtime::Balances::free_balance(¶chain_account), - 1003 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + 1003 * dollar(RELAY_CHAIN_CURRENCY) - ACTUAL_XCM_FEE ); }); } @@ -237,7 +238,7 @@ fn homa_xcm_withdraw_unbonded_from_sub_account_works() { // Final parachain balance is: unbond_withdrew($1000) + initial_endowment($2) - xcm_fee assert_eq!( kusama_runtime::Balances::free_balance(¶chain_account.clone()), - 1002 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + 1002 * dollar(RELAY_CHAIN_CURRENCY) - ACTUAL_XCM_FEE ); }); } @@ -320,7 +321,7 @@ fn homa_xcm_bond_extra_on_sub_account_works() { // XCM fee is paid by the sovereign account. assert_eq!( kusama_runtime::Balances::free_balance(¶chain_account), - 2 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + 2 * dollar(RELAY_CHAIN_CURRENCY) - ACTUAL_XCM_FEE ); }); } @@ -410,7 +411,7 @@ fn homa_xcm_unbond_on_sub_account_works() { // 2 x XCM fee is paid: for Mint and Redeem assert_eq!( kusama_runtime::Balances::free_balance(¶chain_account), - 2 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 * 2 + 2 * dollar(RELAY_CHAIN_CURRENCY) - ACTUAL_XCM_FEE * 2 ); }); } @@ -523,7 +524,7 @@ fn homa_mint_and_redeem_works() { // 2 x XCM fee is paid: for Mint and Redeem assert_eq!( kusama_runtime::Balances::free_balance(¶chain_account), - 3 * dollar(RELAY_CHAIN_CURRENCY) - 373_333_310 + 3 * dollar(RELAY_CHAIN_CURRENCY) - ACTUAL_XCM_FEE ); }); From 2c3d4641bb0a25b114c568f0e7bb1c786fe5245b Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Tue, 14 Dec 2021 10:48:40 +1300 Subject: [PATCH 20/27] Updated some comments in homa integration tests --- runtime/integration-tests/src/homa_xcm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/integration-tests/src/homa_xcm.rs b/runtime/integration-tests/src/homa_xcm.rs index 593610e851..ad3624325c 100644 --- a/runtime/integration-tests/src/homa_xcm.rs +++ b/runtime/integration-tests/src/homa_xcm.rs @@ -34,13 +34,13 @@ const ACTUAL_XCM_FEE: Balance = 639_999_960; fn get_xcm_weight() -> Vec<(HomaXcmOperation, Option, Option)> { vec![ - // Xcm weight = 400_000_000, fee = 373_333_310 + // Xcm weight = 400_000_000, fee = ACTUAL_XCM_FEE (HomaXcmOperation::XtokensTransfer, Some(XCM_WEIGHT), Some(XCM_FEE)), - // Xcm weight = 14_000_000_000, fee = 373_333_310 + // Xcm weight = 14_000_000_000, fee = ACTUAL_XCM_FEE (HomaXcmOperation::XcmWithdrawUnbonded, Some(XCM_WEIGHT), Some(XCM_FEE)), - // Xcm weight = 14_000_000_000, fee = 373_333_310 + // Xcm weight = 14_000_000_000, fee = ACTUAL_XCM_FEE (HomaXcmOperation::XcmBondExtra, Some(XCM_WEIGHT), Some(XCM_FEE)), - // Xcm weight = 14_000_000_000, fee = 373_333_310 + // Xcm weight = 14_000_000_000, fee = ACTUAL_XCM_FEE (HomaXcmOperation::XcmUnbond, Some(XCM_WEIGHT), Some(XCM_FEE)), ] } From 02914644e5a96a39da2da1cdaa303852de97af51 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Mon, 20 Dec 2021 17:21:53 +0800 Subject: [PATCH 21/27] update --- runtime/integration-tests/src/prices.rs | 24 ++++++++++---------- runtime/karura/src/lib.rs | 29 ++++++++++++++++++++++++- runtime/mandala/src/lib.rs | 29 ++++++++++++++++++++++++- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/runtime/integration-tests/src/prices.rs b/runtime/integration-tests/src/prices.rs index c4d7fb0d63..8c591d3aed 100644 --- a/runtime/integration-tests/src/prices.rs +++ b/runtime/integration-tests/src/prices.rs @@ -90,12 +90,12 @@ fn test_update_liquid_currency_price() { set_oracle_price(vec![(RELAY_CHAIN_CURRENCY, relaychain_price)]); - #[cfg(any(feature = "with-mandala-runtime", feature = "with-karura-runtime"))] - assert_ok!(Homa::reset_ledgers( - Origin::root(), - vec![(0, Some(100 * dollar(RELAY_CHAIN_CURRENCY)), None)] - )); - #[cfg(feature = "with-acala-runtime")] + // #[cfg(any(feature = "with-mandala-runtime", feature = "with-karura-runtime"))] + // assert_ok!(Homa::reset_ledgers( + // Origin::root(), + // vec![(0, Some(100 * dollar(RELAY_CHAIN_CURRENCY)), None)] + // )); + // #[cfg(feature = "with-acala-runtime")] assert_ok!(HomaLite::set_total_staking_currency( Origin::root(), 100 * dollar(RELAY_CHAIN_CURRENCY) @@ -106,12 +106,12 @@ fn test_update_liquid_currency_price() { Some(Ratio::saturating_from_rational(100, 1000)) ); - #[cfg(any(feature = "with-mandala-runtime", feature = "with-karura-runtime"))] - assert_ok!(Homa::reset_ledgers( - Origin::root(), - vec![(0, Some(110 * dollar(RELAY_CHAIN_CURRENCY)), None)] - )); - #[cfg(feature = "with-acala-runtime")] + // #[cfg(any(feature = "with-mandala-runtime", feature = "with-karura-runtime"))] + // assert_ok!(Homa::reset_ledgers( + // Origin::root(), + // vec![(0, Some(110 * dollar(RELAY_CHAIN_CURRENCY)), None)] + // )); + // #[cfg(feature = "with-acala-runtime")] assert_ok!(HomaLite::set_total_staking_currency( Origin::root(), 110 * dollar(RELAY_CHAIN_CURRENCY) diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 1eeb7688ab..4630d646ce 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -199,6 +199,30 @@ impl Contains for BaseCallFilter { return true; } + // In the first runtime upgrade of the migration, reveal following. + // Then, In the first runtime upgrade of the migration, delete following. + // if let Call::HomaLite(homa_lite_call) = call { + // match homa_lite_call { + // module_homa_lite::Call::mint { .. } + // | module_homa_lite::Call::mint_for_requests { .. } + // | module_homa_lite::Call::request_redeem { .. } => { + // return false; + // }, + // _ => {} + // } + // } + // if let Call::Homa(homa_call) = call { + // match homa_call { + // module_homa::Call::mint { .. } + // | module_homa::Call::request_redeem { .. } + // | module_homa::Call::fast_match_redeems { .. } + // | module_homa::Call::claim_redemption { .. } => { + // return false; + // }, + // _ => {} + // } + // } + let is_paused = module_transaction_pause::PausedTransactionFilter::::contains(call); if is_paused { // no paused call @@ -823,7 +847,10 @@ impl module_prices::Config for Runtime { type GetStakingCurrencyId = GetStakingCurrencyId; type GetLiquidCurrencyId = GetLiquidCurrencyId; type LockOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type LiquidStakingExchangeRateProvider = Homa; + // In the second runtime upgrade of the migration, delete this line + type LiquidStakingExchangeRateProvider = HomaLite; + // In the second runtime upgrade of the migration, use this line + //type LiquidStakingExchangeRateProvider = Homa; type DEX = Dex; type Currency = Currencies; type Erc20InfoMapping = EvmErc20InfoMapping; diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 411290f2f6..9bddb4f98b 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -209,6 +209,30 @@ parameter_types! { pub struct BaseCallFilter; impl Contains for BaseCallFilter { fn contains(call: &Call) -> bool { + // In the first runtime upgrade of the migration, reveal following. + // Then, In the first runtime upgrade of the migration, delete following. + // if let Call::HomaLite(homa_lite_call) = call { + // match homa_lite_call { + // module_homa_lite::Call::mint { .. } + // | module_homa_lite::Call::mint_for_requests { .. } + // | module_homa_lite::Call::request_redeem { .. } => { + // return false; + // }, + // _ => {} + // } + // } + // if let Call::Homa(homa_call) = call { + // match homa_call { + // module_homa::Call::mint { .. } + // | module_homa::Call::request_redeem { .. } + // | module_homa::Call::fast_match_redeems { .. } + // | module_homa::Call::claim_redemption { .. } => { + // return false; + // }, + // _ => {} + // } + // } + !module_transaction_pause::PausedTransactionFilter::::contains(call) && !matches!(call, Call::Democracy(pallet_democracy::Call::propose { .. }),) } @@ -840,7 +864,10 @@ impl module_prices::Config for Runtime { type GetStakingCurrencyId = GetStakingCurrencyId; type GetLiquidCurrencyId = GetLiquidCurrencyId; type LockOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type LiquidStakingExchangeRateProvider = Homa; + // In the second runtime upgrade of the migration, delete this line + type LiquidStakingExchangeRateProvider = HomaLite; + // In the second runtime upgrade of the migration, use this line + //type LiquidStakingExchangeRateProvider = Homa; type DEX = Dex; type Currency = Currencies; type Erc20InfoMapping = EvmErc20InfoMapping; From ebf1cecfb7089d5746cac5da1dded571eb493b30 Mon Sep 17 00:00:00 2001 From: Acala Benchmarking Bot Date: Mon, 20 Dec 2021 10:31:44 +0000 Subject: [PATCH 22/27] cargo run --release --color=never --bin=acala --features=runtime-benchmarks --features=with-mandala-runtime -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=module_homa --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --template=./templates/runtime-weight-template.hbs --output=./runtime/mandala/src/weights/ --- runtime/mandala/src/weights/module_homa.rs | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/runtime/mandala/src/weights/module_homa.rs b/runtime/mandala/src/weights/module_homa.rs index 9be92c675f..5578ed1859 100644 --- a/runtime/mandala/src/weights/module_homa.rs +++ b/runtime/mandala/src/weights/module_homa.rs @@ -19,16 +19,16 @@ //! Autogenerated weights for module_homa //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("mandala-latest"), DB CACHE: 128 +//! DATE: 2021-12-20, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: // target/release/acala // benchmark -// --chain=mandala-latest +// --chain=dev // --steps=50 // --repeat=20 -// --pallet=* +// --pallet=module_homa // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -45,57 +45,57 @@ use sp_std::marker::PhantomData; /// Weight functions for module_homa. pub struct WeightInfo(PhantomData); -impl module_homa::WeightInfo for WeightInfo { +impl module_homa::WeightInfo for WeightInfo { fn on_initialize() -> Weight { - (6_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) + (5_902_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) } fn on_initialize_with_bump_era() -> Weight { - (422_000_000 as Weight) + (429_675_000 as Weight) .saturating_add(T::DbWeight::get().reads(29 as Weight)) .saturating_add(T::DbWeight::get().writes(15 as Weight)) } fn mint() -> Weight { - (137_000_000 as Weight) + (147_340_000 as Weight) .saturating_add(T::DbWeight::get().reads(11 as Weight)) .saturating_add(T::DbWeight::get().writes(7 as Weight)) } fn request_redeem() -> Weight { - (77_000_000 as Weight) + (83_226_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn fast_match_redeems(n: u32, ) -> Weight { - (7_163_000 as Weight) - // Standard Error: 260_000 - .saturating_add((101_229_000 as Weight).saturating_mul(n as Weight)) + (22_876_000 as Weight) + // Standard Error: 155_000 + .saturating_add((110_226_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(5 as Weight)) .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn claim_redemption() -> Weight { - (111_000_000 as Weight) + (129_588_000 as Weight) .saturating_add(T::DbWeight::get().reads(10 as Weight)) - .saturating_add(T::DbWeight::get().writes(7 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) } fn update_homa_params() -> Weight { - (66_000_000 as Weight) + (57_414_000 as Weight) .saturating_add(T::DbWeight::get().writes(6 as Weight)) - } + } fn update_bump_era_params() -> Weight { - (29_000_000 as Weight) + (25_804_000 as Weight) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn reset_ledgers(n: u32, ) -> Weight { - (2_268_000 as Weight) - // Standard Error: 245_000 - .saturating_add((19_990_000 as Weight).saturating_mul(n as Weight)) + (8_289_000 as Weight) + // Standard Error: 160_000 + .saturating_add((18_479_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn reset_current_era() -> Weight { - (22_000_000 as Weight) + (20_317_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } From ce154242798933180c688016e2b90ce74e9e5feb Mon Sep 17 00:00:00 2001 From: Acala Benchmarking Bot Date: Mon, 20 Dec 2021 10:38:37 +0000 Subject: [PATCH 23/27] cargo run --release --color=never --bin=acala --features=runtime-benchmarks --features=with-karura-runtime -- benchmark --chain=karura-dev --steps=50 --repeat=20 --pallet=module_homa --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --template=./templates/runtime-weight-template.hbs --output=./runtime/karura/src/weights/ --- runtime/karura/src/weights/module_homa.rs | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/runtime/karura/src/weights/module_homa.rs b/runtime/karura/src/weights/module_homa.rs index 50797441e7..b30829333b 100644 --- a/runtime/karura/src/weights/module_homa.rs +++ b/runtime/karura/src/weights/module_homa.rs @@ -19,7 +19,7 @@ //! Autogenerated weights for module_homa //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-12-20, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("karura-dev"), DB CACHE: 128 // Executed Command: @@ -28,7 +28,7 @@ // --chain=karura-dev // --steps=50 // --repeat=20 -// --pallet=* +// --pallet=module_homa // --extrinsic=* // --execution=wasm // --wasm-execution=compiled @@ -45,57 +45,57 @@ use sp_std::marker::PhantomData; /// Weight functions for module_homa. pub struct WeightInfo(PhantomData); -impl module_homa::WeightInfo for WeightInfo { +impl module_homa::WeightInfo for WeightInfo { fn on_initialize() -> Weight { - (6_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) + (5_884_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) } fn on_initialize_with_bump_era() -> Weight { - (422_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(29 as Weight)) - .saturating_add(T::DbWeight::get().writes(15 as Weight)) + (490_360_000 as Weight) + .saturating_add(T::DbWeight::get().reads(32 as Weight)) + .saturating_add(T::DbWeight::get().writes(16 as Weight)) } fn mint() -> Weight { - (137_000_000 as Weight) + (150_048_000 as Weight) .saturating_add(T::DbWeight::get().reads(11 as Weight)) .saturating_add(T::DbWeight::get().writes(7 as Weight)) } fn request_redeem() -> Weight { - (77_000_000 as Weight) + (84_397_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn fast_match_redeems(n: u32, ) -> Weight { - (7_163_000 as Weight) - // Standard Error: 260_000 - .saturating_add((101_229_000 as Weight).saturating_mul(n as Weight)) + (593_000 as Weight) + // Standard Error: 217_000 + .saturating_add((114_002_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes(5 as Weight)) .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) } fn claim_redemption() -> Weight { - (111_000_000 as Weight) + (132_187_000 as Weight) .saturating_add(T::DbWeight::get().reads(10 as Weight)) - .saturating_add(T::DbWeight::get().writes(7 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) } fn update_homa_params() -> Weight { - (66_000_000 as Weight) + (63_205_000 as Weight) .saturating_add(T::DbWeight::get().writes(6 as Weight)) - } + } fn update_bump_era_params() -> Weight { - (29_000_000 as Weight) + (26_983_000 as Weight) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn reset_ledgers(n: u32, ) -> Weight { - (2_268_000 as Weight) - // Standard Error: 245_000 - .saturating_add((19_990_000 as Weight).saturating_mul(n as Weight)) + (22_598_000 as Weight) + // Standard Error: 604_000 + .saturating_add((18_501_000 as Weight).saturating_mul(n as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(n as Weight))) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) } fn reset_current_era() -> Weight { - (22_000_000 as Weight) + (21_189_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } From 53ca1db11af297c64914bee0bb177c3a2bcf2481 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Fri, 31 Dec 2021 14:34:29 +0800 Subject: [PATCH 24/27] update --- modules/homa/src/lib.rs | 111 ++++++++++----------- modules/homa/src/mock.rs | 14 +++ modules/homa/src/tests.rs | 83 ++++++--------- modules/homa/src/weights.rs | 16 +-- runtime/integration-tests/src/homa_xcm.rs | 6 -- runtime/karura/src/lib.rs | 5 + runtime/karura/src/weights/module_homa.rs | 2 +- runtime/mandala/src/benchmarking/homa.rs | 10 +- runtime/mandala/src/lib.rs | 5 + runtime/mandala/src/weights/module_homa.rs | 2 +- 10 files changed, 115 insertions(+), 139 deletions(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 7cf57bfb93..f08bfed345 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -27,9 +27,11 @@ use module_support::{ExchangeRate, ExchangeRateProvider, HomaSubAccountXcm, Rate use orml_traits::MultiCurrency; use primitives::{Balance, CurrencyId, EraIndex}; use scale_info::TypeInfo; -use sp_arithmetic::traits::CheckedRem; use sp_runtime::{ - traits::{AccountIdConversion, Bounded, CheckedAdd, CheckedDiv, One, Saturating, UniqueSaturatedInto, Zero}, + traits::{ + AccountIdConversion, BlockNumberProvider, Bounded, CheckedDiv, CheckedSub, One, Saturating, + UniqueSaturatedInto, Zero, + }, ArithmeticError, FixedPointNumber, }; use sp_std::{cmp::Ordering, convert::From, prelude::*, vec, vec::Vec}; @@ -45,6 +47,8 @@ pub mod weights; pub mod module { use super::*; + pub type RelayChainBlockNumberOf = <::RelayChainBlockNumber as BlockNumberProvider>::BlockNumber; + /// The subaccount's staking ledger which kept by Homa protocol #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default)] pub struct StakingLedger { @@ -133,6 +137,17 @@ pub mod module { #[pallet::constant] type BondingDuration: Get; + /// The staking amount of threshold to mint. + #[pallet::constant] + type MintThreshold: Get; + + /// The liquid amount of threshold to redeem. + #[pallet::constant] + type RedeemThreshold: Get; + + /// Block number provider for the relaychain. + type RelayChainBlockNumber: BlockNumberProvider; + /// The HomaXcm to manage the staking of sub-account on relaychain. type HomaXcm: HomaSubAccountXcm; @@ -189,18 +204,14 @@ pub mod module { /// The estimated reward rate per era of relaychain staking has been updated. /// \[reward_rate\] EstimatedRewardRatePerEraUpdated(Rate), - /// The threshold to mint has been updated. \[mint_threshold\] - MintThresholdUpdated(Balance), - /// The threshold to redeem has been updated. \[redeem_threshold\] - RedeemThresholdUpdated(Balance), /// The commission rate has been updated. \[commission_rate\] CommissionRateUpdated(Rate), /// The fast match fee rate has been updated. \[commission_rate\] FastMatchFeeRateUpdated(Rate), - /// The prefix to bump era has been updated. \[prefix_blocks\] - BumpEraPrefixUpdated(T::BlockNumber), - /// The frequency to bump era has been updated. \[frequency_blocks\] - BumpEraFrequencyUpdated(T::BlockNumber), + /// The relaychain block number of last era bumped updated. \[last_era_bumped_block\] + LastEraBumpedBlockUpdated(RelayChainBlockNumberOf), + /// The frequency to bump era has been updated. \[frequency\] + BumpEraFrequencyUpdated(RelayChainBlockNumberOf), } /// The current era of relaychain @@ -271,7 +282,7 @@ pub mod module { #[pallet::getter(fn estimated_reward_rate_per_era)] pub type EstimatedRewardRatePerEra = StorageValue<_, Rate, ValueQuery>; - /// Th maximum amount of bonded staking currency for a single sub on relaychain to obtain the + /// The maximum amount of bonded staking currency for a single sub on relaychain to obtain the /// best staking rewards. /// /// SoftBondedCapPerSubAccount: value: Balance @@ -279,20 +290,6 @@ pub mod module { #[pallet::getter(fn soft_bonded_cap_per_sub_account)] pub type SoftBondedCapPerSubAccount = StorageValue<_, Balance, ValueQuery>; - /// Th staking amount of threshold to mint. - /// - /// MintThreshold: value: Balance - #[pallet::storage] - #[pallet::getter(fn mint_threshold)] - pub type MintThreshold = StorageValue<_, Balance, ValueQuery>; - - /// Th liquid amount of threshold to redeem. - /// - /// RedeemThreshold: value: Balance - #[pallet::storage] - #[pallet::getter(fn redeem_threshold)] - pub type RedeemThreshold = StorageValue<_, Balance, ValueQuery>; - /// The rate of Homa drawn from the staking reward as commission. /// The draw will be transfer to TreasuryAccount of Homa in liquid currency. /// @@ -308,25 +305,28 @@ pub mod module { #[pallet::getter(fn fast_match_fee_rate)] pub type FastMatchFeeRate = StorageValue<_, Rate, ValueQuery>; - /// The prefix of block numbers to bump local current era. + /// The relaychain block number of last era bumped. /// - /// BumpEraPrefix: value: BlockNumber + /// LastEraBumpedBlock: value: RelayChainBlockNumberOf #[pallet::storage] - #[pallet::getter(fn bump_era_prefix)] - pub type BumpEraPrefix = StorageValue<_, T::BlockNumber, ValueQuery>; + #[pallet::getter(fn last_era_bumped_block)] + pub type LastEraBumpedBlock = StorageValue<_, RelayChainBlockNumberOf, ValueQuery>; - /// The internal of blocks numbers to bump local current era. + /// The internal of relaychain block number of relaychain to bump local current era. + /// + /// LastEraBumpedRelayChainBlock: value: RelayChainBlockNumberOf #[pallet::storage] #[pallet::getter(fn bump_era_frequency)] - pub type BumpEraFrequency = StorageValue<_, T::BlockNumber, ValueQuery>; + pub type BumpEraFrequency = StorageValue<_, RelayChainBlockNumberOf, ValueQuery>; #[pallet::pallet] pub struct Pallet(_); #[pallet::hooks] impl Hooks for Pallet { - fn on_initialize(n: T::BlockNumber) -> Weight { - if Self::should_bump_local_current_era(n) { + fn on_initialize(_: T::BlockNumber) -> Weight { + let relay_chain_current_block = T::RelayChainBlockNumber::current_block_number(); + if Self::should_bump_local_current_era(relay_chain_current_block) { let _ = Self::bump_current_era(); ::WeightInfo::on_initialize_with_bump_era() } else { @@ -346,8 +346,8 @@ pub mod module { pub fn mint(origin: OriginFor, #[pallet::compact] amount: Balance) -> DispatchResult { let minter = ensure_signed(origin)?; - // Ensure the amount is above the mint threshold. - ensure!(amount >= Self::mint_threshold(), Error::::BelowMintThreshold); + // Ensure the amount is above the MintThreshold. + ensure!(amount >= T::MintThreshold::get(), Error::::BelowMintThreshold); // Ensure the total staking currency will not exceed soft cap. ensure!( @@ -407,7 +407,7 @@ pub mod module { let liquid_currency_id = T::LiquidCurrencyId::get(); ensure!( - (!previous_request_amount.is_zero() && amount.is_zero()) || amount >= Self::redeem_threshold(), + (!previous_request_amount.is_zero() && amount.is_zero()) || amount >= T::RedeemThreshold::get(), Error::::BelowRedeemThreshold ); @@ -508,8 +508,6 @@ pub mod module { /// on relaychain to obtain the best staking rewards. /// - `estimated_reward_rate_per_era`: the estimated staking yield of each era on the /// current relay chain. - /// - `mint_threshold`: the staking currency amount of threshold when mint. - /// - `redeem_threshold`: the liquid currency amount of threshold when request redeem. /// - `commission_rate`: the rate to draw from estimated staking rewards as commission to /// HomaTreasury /// - `fast_match_fee_rate`: the fixed fee rate when redeem request is been fast matched. @@ -519,8 +517,6 @@ pub mod module { origin: OriginFor, soft_bonded_cap_per_sub_account: Option, estimated_reward_rate_per_era: Option, - mint_threshold: Option, - redeem_threshold: Option, commission_rate: Option, fast_match_fee_rate: Option, ) -> DispatchResult { @@ -534,14 +530,6 @@ pub mod module { EstimatedRewardRatePerEra::::put(change); Self::deposit_event(Event::::EstimatedRewardRatePerEraUpdated(change)); } - if let Some(change) = mint_threshold { - MintThreshold::::put(change); - Self::deposit_event(Event::::MintThresholdUpdated(change)); - } - if let Some(change) = redeem_threshold { - RedeemThreshold::::put(change); - Self::deposit_event(Event::::RedeemThresholdUpdated(change)); - } if let Some(change) = commission_rate { CommissionRate::::put(change); Self::deposit_event(Event::::CommissionRateUpdated(change)); @@ -558,20 +546,20 @@ pub mod module { /// Requires `GovernanceOrigin` /// /// Parameters: - /// - `prefix`: the prefix of block number on parachain. + /// - `fix_last_era_bumped_block`: fix the relaychain block number of last era bumped. /// - `frequency`: the frequency of block number on parachain. #[pallet::weight(< T as Config >::WeightInfo::update_bump_era_params())] #[transactional] pub fn update_bump_era_params( origin: OriginFor, - prefix: Option, + last_era_bumped_block: Option, frequency: Option, ) -> DispatchResult { T::GovernanceOrigin::ensure_origin(origin)?; - if let Some(change) = prefix { - BumpEraPrefix::::put(change); - Self::deposit_event(Event::::BumpEraPrefixUpdated(change)); + if let Some(change) = last_era_bumped_block { + LastEraBumpedBlock::::put(change); + Self::deposit_event(Event::::LastEraBumpedBlockUpdated(change)); } if let Some(change) = frequency { BumpEraFrequency::::put(change); @@ -741,7 +729,7 @@ pub mod module { request_amount } else { // if cannot fast match the request amount fully, at least keep RedeemThreshold as remainder. - liquid_limit_at_fee_rate.min(request_amount.saturating_sub(Self::redeem_threshold())) + liquid_limit_at_fee_rate.min(request_amount.saturating_sub(T::RedeemThreshold::get())) }; if !actual_liquid_to_redeem.is_zero() { @@ -872,8 +860,8 @@ pub mod module { pub fn process_to_bond_pool() -> DispatchResult { let to_bond_pool = Self::to_bond_pool(); - // if to_bond is gte than mint_threshold, try to bond_extra on relaychain - if to_bond_pool >= Self::mint_threshold() { + // if to_bond is gte than MintThreshold, try to bond_extra on relaychain + if to_bond_pool >= T::MintThreshold::get() { let xcm_transfer_fee = T::HomaXcm::get_xcm_transfer_fee(); let bonded_list: Vec<(u16, Balance)> = T::ActiveSubAccountsIndexList::get() .iter() @@ -968,11 +956,11 @@ pub mod module { T::Currency::withdraw(T::LiquidCurrencyId::get(), &Self::account_id(), total_redeem_amount) } - pub fn should_bump_local_current_era(block_number: T::BlockNumber) -> bool { - block_number - .checked_add(&Self::bump_era_prefix()) - .and_then(|n| n.checked_rem(&Self::bump_era_frequency())) - .map_or(false, |n| n.is_zero()) + pub fn should_bump_local_current_era(relaychain_block_number: RelayChainBlockNumberOf) -> bool { + relaychain_block_number + .checked_sub(&Self::last_era_bumped_block()) + .and_then(|n| n.checked_div(&Self::bump_era_frequency())) + .map_or(false, |n| n >= One::one()) } /// Bump current era. @@ -983,6 +971,7 @@ pub mod module { let previous_era = Self::relay_chain_current_era(); let new_era = previous_era + 1; RelayChainCurrentEra::::put(new_era); + LastEraBumpedBlock::::put(T::RelayChainBlockNumber::current_block_number()); Self::deposit_event(Event::::CurrentEraBumped(new_era)); // Rebalance: diff --git a/modules/homa/src/mock.rs b/modules/homa/src/mock.rs index 3f9469ada6..ab71634412 100644 --- a/modules/homa/src/mock.rs +++ b/modules/homa/src/mock.rs @@ -154,6 +154,14 @@ impl module_currencies::Config for Runtime { type OnDust = (); } +impl BlockNumberProvider for MockRelayBlockNumberProvider { + type BlockNumber = BlockNumber; + + fn current_block_number() -> Self::BlockNumber { + Self::get() + } +} + ord_parameter_types! { pub const HomaAdmin: AccountId = DAVE; } @@ -166,6 +174,9 @@ parameter_types! { pub DefaultExchangeRate: ExchangeRate = ExchangeRate::saturating_from_rational(1, 10); pub ActiveSubAccountsIndexList: Vec = vec![0, 1, 2]; pub const BondingDuration: EraIndex = 28; + pub static MintThreshold: Balance = 0; + pub static RedeemThreshold: Balance = 0; + pub static MockRelayBlockNumberProvider: BlockNumber = 0; } impl Config for Runtime { @@ -179,6 +190,9 @@ impl Config for Runtime { type DefaultExchangeRate = DefaultExchangeRate; type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; type BondingDuration = BondingDuration; + type MintThreshold = MintThreshold; + type RedeemThreshold = RedeemThreshold; + type RelayChainBlockNumber = MockRelayBlockNumberProvider; type HomaXcm = MockHomaSubAccountXcm; type WeightInfo = (); } diff --git a/modules/homa/src/tests.rs b/modules/homa/src/tests.rs index 57c4ade727..5e118683cc 100644 --- a/modules/homa/src/tests.rs +++ b/modules/homa/src/tests.rs @@ -39,11 +39,10 @@ fn mint_works() { Origin::signed(HomaAdmin::get()), Some(1_000_000), None, - Some(100_000), - None, None, None, )); + MintThreshold::set(100_000); assert_noop!( Homa::mint(Origin::signed(ALICE), 99_999), @@ -88,8 +87,6 @@ fn mint_works() { Some(Rate::saturating_from_rational(10, 100)), None, None, - None, - None, )); assert_eq!(Currencies::free_balance(LIQUID_CURRENCY_ID, &BOB), 0); assert_eq!(Currencies::free_balance(STAKING_CURRENCY_ID, &BOB), 1_000_000); @@ -120,15 +117,7 @@ fn request_redeem_works() { ]) .build() .execute_with(|| { - assert_ok!(Homa::update_homa_params( - Origin::signed(HomaAdmin::get()), - None, - None, - None, - Some(1_000_000), - None, - None, - )); + RedeemThreshold::set(1_000_000); assert_noop!( Homa::request_redeem(Origin::signed(ALICE), 999_999, false), @@ -238,14 +227,12 @@ fn claim_redemption_works() { fn update_homa_params_works() { ExtBuilder::default().build().execute_with(|| { assert_noop!( - Homa::update_homa_params(Origin::signed(ALICE), None, None, None, None, None, None), + Homa::update_homa_params(Origin::signed(ALICE), None, None, None, None), BadOrigin ); assert_eq!(Homa::soft_bonded_cap_per_sub_account(), 0); assert_eq!(Homa::estimated_reward_rate_per_era(), Rate::zero()); - assert_eq!(Homa::mint_threshold(), 0); - assert_eq!(Homa::redeem_threshold(), 0); assert_eq!(Homa::commission_rate(), Rate::zero()); assert_eq!(Homa::fast_match_fee_rate(), Rate::zero()); @@ -253,8 +240,6 @@ fn update_homa_params_works() { Origin::signed(HomaAdmin::get()), Some(1_000_000_000), Some(Rate::saturating_from_rational(1, 10000)), - Some(1_000_000), - Some(10_000_000), Some(Rate::saturating_from_rational(5, 100)), Some(Rate::saturating_from_rational(1, 100)), )); @@ -264,8 +249,6 @@ fn update_homa_params_works() { System::assert_has_event(Event::Homa(crate::Event::EstimatedRewardRatePerEraUpdated( Rate::saturating_from_rational(1, 10000), ))); - System::assert_has_event(Event::Homa(crate::Event::MintThresholdUpdated(1_000_000))); - System::assert_has_event(Event::Homa(crate::Event::RedeemThresholdUpdated(10_000_000))); System::assert_has_event(Event::Homa(crate::Event::CommissionRateUpdated( Rate::saturating_from_rational(5, 100), ))); @@ -277,8 +260,6 @@ fn update_homa_params_works() { Homa::estimated_reward_rate_per_era(), Rate::saturating_from_rational(1, 10000) ); - assert_eq!(Homa::mint_threshold(), 1_000_000); - assert_eq!(Homa::redeem_threshold(), 10_000_000); assert_eq!(Homa::commission_rate(), Rate::saturating_from_rational(5, 100)); assert_eq!(Homa::fast_match_fee_rate(), Rate::saturating_from_rational(1, 100)); }); @@ -291,7 +272,7 @@ fn update_bump_era_params_works() { Homa::update_bump_era_params(Origin::signed(ALICE), None, None), BadOrigin ); - assert_eq!(Homa::bump_era_prefix(), 0); + assert_eq!(Homa::last_era_bumped_block(), 0); assert_eq!(Homa::bump_era_frequency(), 0); assert_ok!(Homa::update_bump_era_params( @@ -299,9 +280,9 @@ fn update_bump_era_params_works() { Some(10), Some(7200), )); - System::assert_has_event(Event::Homa(crate::Event::BumpEraPrefixUpdated(10))); + System::assert_has_event(Event::Homa(crate::Event::LastEraBumpedBlockUpdated(10))); System::assert_has_event(Event::Homa(crate::Event::BumpEraFrequencyUpdated(7200))); - assert_eq!(Homa::bump_era_prefix(), 10); + assert_eq!(Homa::last_era_bumped_block(), 10); assert_eq!(Homa::bump_era_frequency(), 7200); }); } @@ -584,10 +565,9 @@ fn do_fast_match_redeem_works() { Some(5_000_000), None, None, - Some(1_000_000), - None, Some(Rate::saturating_from_rational(1, 10)), )); + RedeemThreshold::set(1_000_000); assert_ok!(Homa::mint(Origin::signed(CHARLIE), 1_000_000)); assert_ok!(Homa::request_redeem(Origin::signed(ALICE), 5_000_000, true)); assert_ok!(Homa::request_redeem(Origin::signed(BOB), 6_500_000, true)); @@ -685,8 +665,6 @@ fn process_staking_rewards_works() { Some(Rate::saturating_from_rational(20, 100)), None, None, - None, - None, )); assert_eq!( Homa::staking_ledgers(0), @@ -730,8 +708,6 @@ fn process_staking_rewards_works() { Origin::signed(HomaAdmin::get()), None, None, - None, - None, Some(Rate::saturating_from_rational(10, 100)), None, )); @@ -866,8 +842,6 @@ fn process_to_bond_pool_works() { None, None, None, - None, - None, )); assert_ok!(Homa::reset_ledgers( Origin::signed(HomaAdmin::get()), @@ -971,15 +945,7 @@ fn process_to_bond_pool_works() { // ToBondPool is able to afford xcm_transfer_fee, and below the mint_threshold, no bonded added. assert_ok!(Homa::mint(Origin::signed(ALICE), 2_000_000)); - assert_ok!(Homa::update_homa_params( - Origin::signed(HomaAdmin::get()), - None, - None, - Some(3_000_000), - None, - None, - None, - )); + MintThreshold::set(3_000_000); assert_eq!(Homa::to_bond_pool(), 2_000_000); assert_eq!(Homa::get_total_bonded(), 5_000_000); assert_eq!(Currencies::total_issuance(STAKING_CURRENCY_ID), 13_000_000); @@ -1134,26 +1100,31 @@ fn process_redeem_requests_works() { #[test] fn should_bump_local_current_era_works() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(Homa::bump_era_prefix(), 0); + assert_eq!(Homa::last_era_bumped_block(), 0); assert_eq!(Homa::bump_era_frequency(), 0); + assert_eq!(Homa::should_bump_local_current_era(9), false); assert_eq!(Homa::should_bump_local_current_era(10), false); - assert_eq!(Homa::should_bump_local_current_era(12), false); + assert_eq!(Homa::should_bump_local_current_era(11), false); assert_ok!(Homa::update_bump_era_params( Origin::signed(HomaAdmin::get()), None, - Some(5) + Some(10) )); + assert_eq!(Homa::bump_era_frequency(), 10); + assert_eq!(Homa::should_bump_local_current_era(9), false); assert_eq!(Homa::should_bump_local_current_era(10), true); - assert_eq!(Homa::should_bump_local_current_era(12), false); + assert_eq!(Homa::should_bump_local_current_era(11), true); assert_ok!(Homa::update_bump_era_params( Origin::signed(HomaAdmin::get()), - Some(3), + Some(1), None )); + assert_eq!(Homa::last_era_bumped_block(), 1); + assert_eq!(Homa::should_bump_local_current_era(9), false); assert_eq!(Homa::should_bump_local_current_era(10), false); - assert_eq!(Homa::should_bump_local_current_era(12), true); + assert_eq!(Homa::should_bump_local_current_era(11), true); }); } @@ -1167,13 +1138,13 @@ fn bump_current_era_works() { Origin::signed(HomaAdmin::get()), Some(20_000_000), Some(Rate::saturating_from_rational(1, 100)), - Some(2_000_000), - None, Some(Rate::saturating_from_rational(20, 100)), None, )); + MintThreshold::set(2_000_000); // initial states at era #0 + assert_eq!(Homa::last_era_bumped_block(), 0); assert_eq!(Homa::relay_chain_current_era(), 0); assert_eq!(Homa::staking_ledgers(0), None); assert_eq!(Homa::staking_ledgers(1), None); @@ -1199,8 +1170,10 @@ fn bump_current_era_works() { // bump era to #1, // will process to_bond_pool. + MockRelayBlockNumberProvider::set(100); assert_ok!(Homa::bump_current_era()); System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(1))); + assert_eq!(Homa::last_era_bumped_block(), 100); assert_eq!(Homa::relay_chain_current_era(), 1); assert_eq!( Homa::staking_ledgers(0), @@ -1228,8 +1201,10 @@ fn bump_current_era_works() { // bump era to #2, // accumulate staking reward and draw commission + MockRelayBlockNumberProvider::set(200); assert_ok!(Homa::bump_current_era()); System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(2))); + assert_eq!(Homa::last_era_bumped_block(), 200); assert_eq!(Homa::relay_chain_current_era(), 2); assert_eq!( Homa::staking_ledgers(0), @@ -1265,8 +1240,6 @@ fn bump_current_era_works() { Some(Rate::zero()), None, None, - None, - None, )); // and there's redeem request @@ -1278,6 +1251,7 @@ fn bump_current_era_works() { // bump era to #3, // will process redeem requests + MockRelayBlockNumberProvider::set(300); assert_ok!(Homa::bump_current_era()); System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(3))); System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( @@ -1286,6 +1260,7 @@ fn bump_current_era_works() { 280_000_000, 26_605_824, ))); + assert_eq!(Homa::last_era_bumped_block(), 300); assert_eq!(Homa::relay_chain_current_era(), 3); assert_eq!( Homa::staking_ledgers(0), @@ -1322,10 +1297,12 @@ fn bump_current_era_works() { // bump era to #31, // will process scheduled unbonded - for _ in 3..31 { + for n in 4..32 { + MockRelayBlockNumberProvider::set(n * 100); assert_ok!(Homa::bump_current_era()); } System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(31))); + assert_eq!(Homa::last_era_bumped_block(), 3100); assert_eq!(Homa::relay_chain_current_era(), 31); assert_eq!(Homa::staking_ledgers(0), None); assert_eq!( diff --git a/modules/homa/src/weights.rs b/modules/homa/src/weights.rs index 60426c5037..0f7735ca03 100644 --- a/modules/homa/src/weights.rs +++ b/modules/homa/src/weights.rs @@ -64,7 +64,7 @@ pub struct AcalaWeight(PhantomData); impl WeightInfo for AcalaWeight { fn on_initialize() -> Weight { (6_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) } fn on_initialize_with_bump_era() -> Weight { (422_000_000 as Weight) @@ -93,12 +93,12 @@ impl WeightInfo for AcalaWeight { fn claim_redemption() -> Weight { (111_000_000 as Weight) .saturating_add(T::DbWeight::get().reads(10 as Weight)) - .saturating_add(T::DbWeight::get().writes(7 as Weight)) + .saturating_add(T::DbWeight::get().writes(7 as Weight)) } fn update_homa_params() -> Weight { (66_000_000 as Weight) - .saturating_add(T::DbWeight::get().writes(6 as Weight)) - } + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } fn update_bump_era_params() -> Weight { (29_000_000 as Weight) .saturating_add(T::DbWeight::get().writes(2 as Weight)) @@ -121,7 +121,7 @@ impl WeightInfo for AcalaWeight { impl WeightInfo for () { fn on_initialize() -> Weight { (6_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) } fn on_initialize_with_bump_era() -> Weight { (422_000_000 as Weight) @@ -150,12 +150,12 @@ impl WeightInfo for () { fn claim_redemption() -> Weight { (111_000_000 as Weight) .saturating_add(RocksDbWeight::get().reads(10 as Weight)) - .saturating_add(RocksDbWeight::get().writes(7 as Weight)) + .saturating_add(RocksDbWeight::get().writes(7 as Weight)) } fn update_homa_params() -> Weight { (66_000_000 as Weight) - .saturating_add(RocksDbWeight::get().writes(6 as Weight)) - } + .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + } fn update_bump_era_params() -> Weight { (29_000_000 as Weight) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) diff --git a/runtime/integration-tests/src/homa_xcm.rs b/runtime/integration-tests/src/homa_xcm.rs index ad3624325c..3c0c07ba14 100644 --- a/runtime/integration-tests/src/homa_xcm.rs +++ b/runtime/integration-tests/src/homa_xcm.rs @@ -48,8 +48,6 @@ fn get_xcm_weight() -> Vec<(HomaXcmOperation, Option, Option)> struct HomaParams { pub soft_bonded_cap_per_sub_account: Option, pub estimated_reward_rate_per_era: Option, - pub mint_threshold: Option, - pub redeem_threshold: Option, pub commission_rate: Option, pub fast_match_fee_rate: Option, } @@ -58,8 +56,6 @@ impl Default for HomaParams { HomaParams { soft_bonded_cap_per_sub_account: Some(1_000_000_000 * dollar(RELAY_CHAIN_CURRENCY)), estimated_reward_rate_per_era: None, - mint_threshold: None, - redeem_threshold: None, commission_rate: None, fast_match_fee_rate: None, } @@ -78,8 +74,6 @@ fn configure_homa_and_homa_xcm() { Origin::root(), param.soft_bonded_cap_per_sub_account, param.estimated_reward_rate_per_era, - param.mint_threshold, - param.redeem_threshold, param.commission_rate, param.fast_match_fee_rate, )); diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 70fcd1caf3..35fb83bd35 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -1689,6 +1689,8 @@ parameter_types! { pub HomaTreasuryAccount: AccountId = HomaTreasuryPalletId::get().into_account(); pub ActiveSubAccountsIndexList: Vec = vec![RelayChainSubAccountId::HomaLite as u16]; pub KusamaBondingDuration: EraIndex = 28; + pub MintThreshold: Balance = dollar(KSM); + pub RedeemThreshold: Balance = 10 * dollar(LKSM); } impl module_homa::Config for Runtime { @@ -1702,6 +1704,9 @@ impl module_homa::Config for Runtime { type DefaultExchangeRate = DefaultExchangeRate; type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; type BondingDuration = KusamaBondingDuration; + type MintThreshold = MintThreshold; + type RedeemThreshold = RedeemThreshold; + type RelayChainBlockNumber = RelayChainBlockNumberProvider; type HomaXcm = HomaXcm; type WeightInfo = weights::module_homa::WeightInfo; } diff --git a/runtime/karura/src/weights/module_homa.rs b/runtime/karura/src/weights/module_homa.rs index b30829333b..df91fb2afb 100644 --- a/runtime/karura/src/weights/module_homa.rs +++ b/runtime/karura/src/weights/module_homa.rs @@ -81,7 +81,7 @@ impl module_homa::WeightInfo for WeightInfo { } fn update_homa_params() -> Weight { (63_205_000 as Weight) - .saturating_add(T::DbWeight::get().writes(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn update_bump_era_params() -> Weight { (26_983_000 as Weight) diff --git a/runtime/mandala/src/benchmarking/homa.rs b/runtime/mandala/src/benchmarking/homa.rs index 1e3136b7d1..36f6d3eb2d 100644 --- a/runtime/mandala/src/benchmarking/homa.rs +++ b/runtime/mandala/src/benchmarking/homa.rs @@ -57,8 +57,6 @@ runtime_benchmarks! { RawOrigin::Root.into(), Some(10_000_000_000_000_000), Some(Rate::saturating_from_rational(1, 100)), - None, - None, Some(Rate::saturating_from_rational(20, 100)), None, )?; @@ -80,8 +78,6 @@ runtime_benchmarks! { Some(Rate::saturating_from_rational(1, 10000)), None, None, - None, - None, )?; set_balance(GetStakingCurrencyId::get(), &caller, amount * 2); }: _(RawOrigin::Signed(caller), amount) @@ -106,13 +102,11 @@ runtime_benchmarks! { Some(Rate::saturating_from_rational(1, 10000)), None, None, - None, - None, )?; Homa::mint(RawOrigin::Signed(minter.clone()).into(), mint_amount)?; let mut redeem_request_list: Vec = vec![]; - let redeem_amount = 1_000_000_000_000; + let redeem_amount = 10_000_000_000_000; for i in 0 .. n { let redeemer = account("redeemer", i, SEED); >::transfer(GetLiquidCurrencyId::get(), &minter, &redeemer, redeem_amount * 2)?; @@ -136,8 +130,6 @@ runtime_benchmarks! { RawOrigin::Root, Some(1_000_000_000_000), Some(Rate::saturating_from_rational(1, 100)), - Some(1_000_000_000_000), - Some(1_000_000_000_000), Some(Rate::saturating_from_rational(1, 100)), Some(Rate::saturating_from_rational(1, 100))) diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 494d2e591f..fa0eabddf6 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -1236,6 +1236,8 @@ parameter_types! { pub HomaTreasuryAccount: AccountId = HomaTreasuryPalletId::get().into_account(); pub ActiveSubAccountsIndexList: Vec = vec![RelayChainSubAccountId::HomaLite as u16]; pub RelayChainBondingDuration: EraIndex = 28; + pub MintThreshold: Balance = dollar(DOT); + pub RedeemThreshold: Balance = 10 * dollar(LDOT); } impl module_homa::Config for Runtime { @@ -1249,6 +1251,9 @@ impl module_homa::Config for Runtime { type DefaultExchangeRate = DefaultExchangeRate; type ActiveSubAccountsIndexList = ActiveSubAccountsIndexList; type BondingDuration = RelayChainBondingDuration; + type MintThreshold = MintThreshold; + type RedeemThreshold = RedeemThreshold; + type RelayChainBlockNumber = RelayChainBlockNumberProvider; type HomaXcm = HomaXcm; type WeightInfo = weights::module_homa::WeightInfo; } diff --git a/runtime/mandala/src/weights/module_homa.rs b/runtime/mandala/src/weights/module_homa.rs index 5578ed1859..9059ac7c7e 100644 --- a/runtime/mandala/src/weights/module_homa.rs +++ b/runtime/mandala/src/weights/module_homa.rs @@ -81,7 +81,7 @@ impl module_homa::WeightInfo for WeightInfo { } fn update_homa_params() -> Weight { (57_414_000 as Weight) - .saturating_add(T::DbWeight::get().writes(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn update_bump_era_params() -> Weight { (25_804_000 as Weight) From 38f561927773bf20242199241ff59275bed7088f Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Fri, 31 Dec 2021 14:59:52 +0800 Subject: [PATCH 25/27] bump mandala runtime version --- runtime/mandala/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index d4f57184e6..c9462f98de 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -139,7 +139,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("mandala"), impl_name: create_runtime_str!("mandala"), authoring_version: 1, - spec_version: 2015, + spec_version: 2017, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 5bb7dda5bf9e9922583b8f22977c606fb71596bb Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Fri, 31 Dec 2021 15:50:26 +0800 Subject: [PATCH 26/27] update --- modules/homa/src/lib.rs | 29 +++++++++---------- modules/homa/src/tests.rs | 35 ++++++++++++----------- runtime/integration-tests/src/homa_xcm.rs | 8 +++--- runtime/mandala/src/lib.rs | 2 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index f08bfed345..04ba54baa5 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -47,8 +47,6 @@ pub mod weights; pub mod module { use super::*; - pub type RelayChainBlockNumberOf = <::RelayChainBlockNumber as BlockNumberProvider>::BlockNumber; - /// The subaccount's staking ledger which kept by Homa protocol #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default)] pub struct StakingLedger { @@ -209,9 +207,9 @@ pub mod module { /// The fast match fee rate has been updated. \[commission_rate\] FastMatchFeeRateUpdated(Rate), /// The relaychain block number of last era bumped updated. \[last_era_bumped_block\] - LastEraBumpedBlockUpdated(RelayChainBlockNumberOf), + LastEraBumpedBlockUpdated(T::BlockNumber), /// The frequency to bump era has been updated. \[frequency\] - BumpEraFrequencyUpdated(RelayChainBlockNumberOf), + BumpEraFrequencyUpdated(T::BlockNumber), } /// The current era of relaychain @@ -307,17 +305,17 @@ pub mod module { /// The relaychain block number of last era bumped. /// - /// LastEraBumpedBlock: value: RelayChainBlockNumberOf + /// LastEraBumpedBlock: value: T::BlockNumber #[pallet::storage] #[pallet::getter(fn last_era_bumped_block)] - pub type LastEraBumpedBlock = StorageValue<_, RelayChainBlockNumberOf, ValueQuery>; + pub type LastEraBumpedBlock = StorageValue<_, T::BlockNumber, ValueQuery>; /// The internal of relaychain block number of relaychain to bump local current era. /// - /// LastEraBumpedRelayChainBlock: value: RelayChainBlockNumberOf + /// LastEraBumpedRelayChainBlock: value: T::BlockNumber #[pallet::storage] #[pallet::getter(fn bump_era_frequency)] - pub type BumpEraFrequency = StorageValue<_, RelayChainBlockNumberOf, ValueQuery>; + pub type BumpEraFrequency = StorageValue<_, T::BlockNumber, ValueQuery>; #[pallet::pallet] pub struct Pallet(_); @@ -325,9 +323,9 @@ pub mod module { #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(_: T::BlockNumber) -> Weight { - let relay_chain_current_block = T::RelayChainBlockNumber::current_block_number(); - if Self::should_bump_local_current_era(relay_chain_current_block) { - let _ = Self::bump_current_era(); + let bump_era_number = Self::era_amount_should_to_bump(T::RelayChainBlockNumber::current_block_number()); + if !bump_era_number.is_zero() { + let _ = Self::bump_current_era(bump_era_number); ::WeightInfo::on_initialize_with_bump_era() } else { ::WeightInfo::on_initialize() @@ -956,20 +954,21 @@ pub mod module { T::Currency::withdraw(T::LiquidCurrencyId::get(), &Self::account_id(), total_redeem_amount) } - pub fn should_bump_local_current_era(relaychain_block_number: RelayChainBlockNumberOf) -> bool { + pub fn era_amount_should_to_bump(relaychain_block_number: T::BlockNumber) -> EraIndex { relaychain_block_number .checked_sub(&Self::last_era_bumped_block()) .and_then(|n| n.checked_div(&Self::bump_era_frequency())) - .map_or(false, |n| n >= One::one()) + .and_then(|n| TryInto::::try_into(n).ok()) + .unwrap_or_else(Zero::zero) } /// Bump current era. /// The rebalance will send XCM messages to relaychain. Once the XCM message is sent, /// the execution result cannot be obtained and cannot be rolled back. So the process /// of rebalance is not atomic. - pub fn bump_current_era() -> DispatchResult { + pub fn bump_current_era(amount: EraIndex) -> DispatchResult { let previous_era = Self::relay_chain_current_era(); - let new_era = previous_era + 1; + let new_era = previous_era + amount; RelayChainCurrentEra::::put(new_era); LastEraBumpedBlock::::put(T::RelayChainBlockNumber::current_block_number()); Self::deposit_event(Event::::CurrentEraBumped(new_era)); diff --git a/modules/homa/src/tests.rs b/modules/homa/src/tests.rs index 5e118683cc..837e37f69d 100644 --- a/modules/homa/src/tests.rs +++ b/modules/homa/src/tests.rs @@ -1098,13 +1098,14 @@ fn process_redeem_requests_works() { } #[test] -fn should_bump_local_current_era_works() { +fn era_amount_should_to_bump_works() { ExtBuilder::default().build().execute_with(|| { assert_eq!(Homa::last_era_bumped_block(), 0); assert_eq!(Homa::bump_era_frequency(), 0); - assert_eq!(Homa::should_bump_local_current_era(9), false); - assert_eq!(Homa::should_bump_local_current_era(10), false); - assert_eq!(Homa::should_bump_local_current_era(11), false); + assert_eq!(Homa::era_amount_should_to_bump(9), 0); + assert_eq!(Homa::era_amount_should_to_bump(10), 0); + assert_eq!(Homa::era_amount_should_to_bump(11), 0); + assert_eq!(Homa::era_amount_should_to_bump(30), 0); assert_ok!(Homa::update_bump_era_params( Origin::signed(HomaAdmin::get()), @@ -1112,9 +1113,10 @@ fn should_bump_local_current_era_works() { Some(10) )); assert_eq!(Homa::bump_era_frequency(), 10); - assert_eq!(Homa::should_bump_local_current_era(9), false); - assert_eq!(Homa::should_bump_local_current_era(10), true); - assert_eq!(Homa::should_bump_local_current_era(11), true); + assert_eq!(Homa::era_amount_should_to_bump(9), 0); + assert_eq!(Homa::era_amount_should_to_bump(10), 1); + assert_eq!(Homa::era_amount_should_to_bump(11), 1); + assert_eq!(Homa::era_amount_should_to_bump(30), 3); assert_ok!(Homa::update_bump_era_params( Origin::signed(HomaAdmin::get()), @@ -1122,9 +1124,10 @@ fn should_bump_local_current_era_works() { None )); assert_eq!(Homa::last_era_bumped_block(), 1); - assert_eq!(Homa::should_bump_local_current_era(9), false); - assert_eq!(Homa::should_bump_local_current_era(10), false); - assert_eq!(Homa::should_bump_local_current_era(11), true); + assert_eq!(Homa::era_amount_should_to_bump(9), 0); + assert_eq!(Homa::era_amount_should_to_bump(10), 0); + assert_eq!(Homa::era_amount_should_to_bump(11), 1); + assert_eq!(Homa::era_amount_should_to_bump(30), 2); }); } @@ -1171,7 +1174,7 @@ fn bump_current_era_works() { // bump era to #1, // will process to_bond_pool. MockRelayBlockNumberProvider::set(100); - assert_ok!(Homa::bump_current_era()); + assert_ok!(Homa::bump_current_era(1)); System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(1))); assert_eq!(Homa::last_era_bumped_block(), 100); assert_eq!(Homa::relay_chain_current_era(), 1); @@ -1202,7 +1205,7 @@ fn bump_current_era_works() { // bump era to #2, // accumulate staking reward and draw commission MockRelayBlockNumberProvider::set(200); - assert_ok!(Homa::bump_current_era()); + assert_ok!(Homa::bump_current_era(1)); System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(2))); assert_eq!(Homa::last_era_bumped_block(), 200); assert_eq!(Homa::relay_chain_current_era(), 2); @@ -1252,7 +1255,7 @@ fn bump_current_era_works() { // bump era to #3, // will process redeem requests MockRelayBlockNumberProvider::set(300); - assert_ok!(Homa::bump_current_era()); + assert_ok!(Homa::bump_current_era(1)); System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(3))); System::assert_has_event(Event::Homa(crate::Event::RedeemedByUnbond( ALICE, @@ -1297,10 +1300,8 @@ fn bump_current_era_works() { // bump era to #31, // will process scheduled unbonded - for n in 4..32 { - MockRelayBlockNumberProvider::set(n * 100); - assert_ok!(Homa::bump_current_era()); - } + MockRelayBlockNumberProvider::set(3100); + assert_ok!(Homa::bump_current_era(28)); System::assert_has_event(Event::Homa(crate::Event::CurrentEraBumped(31))); assert_eq!(Homa::last_era_bumped_block(), 3100); assert_eq!(Homa::relay_chain_current_era(), 31); diff --git a/runtime/integration-tests/src/homa_xcm.rs b/runtime/integration-tests/src/homa_xcm.rs index 3c0c07ba14..c36e4564d2 100644 --- a/runtime/integration-tests/src/homa_xcm.rs +++ b/runtime/integration-tests/src/homa_xcm.rs @@ -379,7 +379,7 @@ fn homa_xcm_unbond_on_sub_account_works() { // Amount bonded = $1000 - XCM_FEE = 999_990_000_000_000 assert_ok!(Homa::mint(Origin::signed(bob()), 1_000 * dollar(RELAY_CHAIN_CURRENCY),)); // Update internal storage in Homa - assert_ok!(Homa::bump_current_era()); + assert_ok!(Homa::bump_current_era(1)); // Put in redeem request assert_ok!(Homa::request_redeem( @@ -489,7 +489,7 @@ fn homa_mint_and_redeem_works() { assert_eq!(Homa::get_total_staking_currency(), 2_000 * dollar(RELAY_CHAIN_CURRENCY)); // Synchronize with Relay chain via Xcm messages. Also update internal storage. - assert_ok!(Homa::bump_current_era()); + assert_ok!(Homa::bump_current_era(1)); assert_eq!( Tokens::free_balance(LIQUID_CURRENCY, &AccountId::from(alice())), @@ -551,7 +551,7 @@ fn homa_mint_and_redeem_works() { )); // Unbonds the tokens on the Relay chain. - assert_ok!(Homa::bump_current_era()); + assert_ok!(Homa::bump_current_era(1)); let unbonding_era = Homa::relay_chain_current_era() + KusamaBondingDuration::get(); assert_eq!(unbonding_era, 30); @@ -595,7 +595,7 @@ fn homa_mint_and_redeem_works() { // Wait for the chunk to unlock for _ in 0..KusamaBondingDuration::get() + 1 { - assert_ok!(Homa::bump_current_era()); + assert_ok!(Homa::bump_current_era(1)); } // Claim the unlocked chunk diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index c9462f98de..9079268ba7 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -139,7 +139,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("mandala"), impl_name: create_runtime_str!("mandala"), authoring_version: 1, - spec_version: 2017, + spec_version: 2018, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 9b003f4c1f606f738990ee3111c84735c4e6f414 Mon Sep 17 00:00:00 2001 From: wangjj9219 <183318287@qq.com> Date: Sun, 2 Jan 2022 09:53:20 +0800 Subject: [PATCH 27/27] update --- modules/homa/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/homa/src/lib.rs b/modules/homa/src/lib.rs index 04ba54baa5..233a1c9e0a 100644 --- a/modules/homa/src/lib.rs +++ b/modules/homa/src/lib.rs @@ -968,7 +968,7 @@ pub mod module { /// of rebalance is not atomic. pub fn bump_current_era(amount: EraIndex) -> DispatchResult { let previous_era = Self::relay_chain_current_era(); - let new_era = previous_era + amount; + let new_era = previous_era.saturating_add(amount); RelayChainCurrentEra::::put(new_era); LastEraBumpedBlock::::put(T::RelayChainBlockNumber::current_block_number()); Self::deposit_event(Event::::CurrentEraBumped(new_era));