diff --git a/Cargo.lock b/Cargo.lock index b984e532fc..ba3923da3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -640,6 +640,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "sp-std", ] [[package]] @@ -722,6 +723,7 @@ dependencies = [ name = "bifrost-minter-reward" version = "0.8.0" dependencies = [ + "bifrost-vtoken-mint", "fixed", "frame-support", "frame-system", @@ -909,11 +911,11 @@ dependencies = [ "orml-tokens", "orml-traits", "pallet-balances", - "pallet-randomness-collective-flip", "parity-scale-codec", "sp-core", "sp-io", "sp-runtime", + "sp-std", "zenlink-protocol", ] diff --git a/node/service/src/chain_spec/asgard.rs b/node/service/src/chain_spec/asgard.rs index fbb864ff96..de21534eba 100644 --- a/node/service/src/chain_spec/asgard.rs +++ b/node/service/src/chain_spec/asgard.rs @@ -29,7 +29,7 @@ use node_primitives::{CurrencyId, TokenSymbol}; use sc_service::ChainType; use sc_telemetry::TelemetryEndpoints; use sp_core::{crypto::UncheckedInto, sr25519}; -use sp_runtime::{traits::Zero, Permill}; +use sp_runtime::traits::Zero; use super::TELEMETRY_URL; use crate::chain_spec::{get_account_id_from_seed, get_from_seed, RelayExtensions}; @@ -99,13 +99,13 @@ pub fn asgard_genesis( ], }, minter_reward: MinterRewardConfig { - wegiths: vec![ + currency_weights: vec![ (CurrencyId::Token(TokenSymbol::DOT), 1 * 1), (CurrencyId::Token(TokenSymbol::ETH), 1 * 1), (CurrencyId::Token(TokenSymbol::KSM), 1 * 3), ], - reward_by_one_block: 5 * DOLLARS / 100, - round_index: 1, + reward_per_block: 5 * DOLLARS / 100, + cycle_index: 1, }, vtoken_mint: VtokenMintConfig { pools: vec![ @@ -121,16 +121,6 @@ pub fn asgard_genesis( (CurrencyId::Token(TokenSymbol::ETH), 14 * DAYS), (CurrencyId::Token(TokenSymbol::KSM), 7 * DAYS), ], - rate_of_interest_each_block: vec![ - (CurrencyId::Token(TokenSymbol::DOT), 019_025_875_190), // 100000.0 * 0.148/(365*24*600) - (CurrencyId::Token(TokenSymbol::ETH), 009_512_937_595), // 50000.0 * 0.082/(365*24*600) - (CurrencyId::Token(TokenSymbol::KSM), 000_285_388_127), // 10000.0 * 0.15/(365*24*600) - ], - yield_rate: vec![ - (CurrencyId::Token(TokenSymbol::DOT), Permill::from_perthousand(148)), // 14.8% - (CurrencyId::Token(TokenSymbol::ETH), Permill::from_perthousand(82)), // 8.2% - (CurrencyId::Token(TokenSymbol::KSM), Permill::from_perthousand(150)), // 15.0% - ], }, } } diff --git a/pallets/bancor/Cargo.toml b/pallets/bancor/Cargo.toml index cf406c238b..f86fa3683d 100644 --- a/pallets/bancor/Cargo.toml +++ b/pallets/bancor/Cargo.toml @@ -17,10 +17,12 @@ orml-traits = { version = "0.4.1-dev", default-features = false } orml-tokens = { version = "0.4.1-dev", default-features = false } node-primitives = { path = "../../node/primitives", default-features = false } num-bigint = { version = "0.4", default-features = false } +sp-std = { version = "3.0.0", default-features = false } [dev-dependencies] sp-core = "3.0.0" sp-io = "3.0.0" +sp-std = "3.0.0" [features] default = ["std"] @@ -34,5 +36,6 @@ std = [ "orml-traits/std", "orml-tokens/std", "node-primitives/std", - "num-bigint/std" + "num-bigint/std", + "sp-std/std", ] \ No newline at end of file diff --git a/pallets/bancor/src/lib.rs b/pallets/bancor/src/lib.rs index 80f20ccd9c..4bbd485558 100644 --- a/pallets/bancor/src/lib.rs +++ b/pallets/bancor/src/lib.rs @@ -20,17 +20,19 @@ use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; -use node_primitives::{traits::BancorHandler, CurrencyId, TokenSymbol}; +use node_primitives::{traits::BancorHandler, CurrencyId}; use num_bigint::BigUint; use orml_traits::MultiCurrency; -use sp_arithmetic::per_things::{PerThing, Perbill}; +use sp_arithmetic::per_things::{PerThing, Perbill, Percent}; use sp_runtime::{ traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Saturating, Zero}, SaturatedConversion, }; +use weights::WeightInfo; mod mock; mod tests; +pub mod weights; pub use pallet::*; @@ -38,17 +40,16 @@ type AccountIdOf = ::AccountId; type BalanceOf = <::MultiCurrenciesHandler as MultiCurrency>>::Balance; -const TWELVE_TEN: u128 = 1_000_000_000_000; const BILLION: u128 = 1_000_000_000; #[derive(Encode, Decode, Clone, Eq, PartialEq, Debug)] pub struct BancorPool { - pub(crate) currency_id: CurrencyId, // ksm, dot, etc. - pub(crate) token_pool: Balance, // token supply of the pool - pub(crate) vstoken_pool: Balance, // vstoken balance of the pool - pub(crate) token_ceiling: Balance, // token available for sale - pub(crate) token_base_supply: Balance, // initial virtual supply of token for the pool - pub(crate) vstoken_base_supply: Balance, // initial virtual balance of vstoken for the pool + currency_id: CurrencyId, // ksm, dot, etc. + token_pool: Balance, // token supply of the pool + vstoken_pool: Balance, // vstoken balance of the pool + token_ceiling: Balance, // token available for sale + token_base_supply: Balance, // initial virtual supply of token for the pool + vstoken_base_supply: Balance, // initial virtual balance of vstoken for the pool } #[frame_support::pallet] @@ -60,10 +61,11 @@ pub mod pallet { type Event: From> + IsType<::Event>; type MultiCurrenciesHandler: MultiCurrency, CurrencyId = CurrencyId>; - // This constant is a percentage number but in an integer presentation. When used, should be - // divided by 100. #[pallet::constant] - type InterventionPercentage: Get>; + type InterventionPercentage: Get; + + /// Set default weight. + type WeightInfo: WeightInfo; } #[pallet::error] @@ -73,8 +75,8 @@ pub mod pallet { AmountNotGreaterThanZero, BancorPoolNotExist, ConversionError, - TokenSupplyNotEnought, - VSTokenSupplyNotEnought, + TokenSupplyNotEnough, + VSTokenSupplyNotEnough, PriceNotQualified, CalculationOverflow, } @@ -93,8 +95,7 @@ pub mod pallet { // key is token, value is BancorPool struct. #[pallet::storage] #[pallet::getter(fn get_bancor_pool)] - pub type BancorPools = - StorageMap>>; + pub type BancorPools = StorageMap<_, Blake2_128Concat, CurrencyId, BancorPool>>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -104,18 +105,7 @@ pub mod pallet { #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { - Self { - bancor_pools: vec![ - ( - CurrencyId::Token(TokenSymbol::DOT), - BalanceOf::::saturated_from(10_000 * TWELVE_TEN as u128), - ), - ( - CurrencyId::Token(TokenSymbol::KSM), - BalanceOf::::saturated_from(1_000_000 * TWELVE_TEN as u128), - ), - ], - } + Self { bancor_pools: vec![] } } } @@ -146,13 +136,13 @@ pub mod pallet { #[pallet::call] impl Pallet { // exchange vstoken for token - #[pallet::weight(1_000)] + #[pallet::weight(T::WeightInfo::exchange_for_token())] pub fn exchange_for_token( origin: OriginFor, currency_id: CurrencyId, vstoken_amount: BalanceOf, token_out_min: BalanceOf, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { // Check origin let exchanger = ensure_signed(origin)?; let vstoken_id = currency_id.to_vstoken().map_err(|_| Error::::ConversionError)?; @@ -183,13 +173,13 @@ pub mod pallet { } // exchange token for vstoken - #[pallet::weight(1_000)] + #[pallet::weight(T::WeightInfo::exchange_for_vstoken())] pub fn exchange_for_vstoken( origin: OriginFor, currency_id: CurrencyId, token_amount: BalanceOf, vstoken_out_min: BalanceOf, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { // Check origin let exchanger = ensure_signed(origin)?; let vstoken_id = currency_id.to_vstoken().map_err(|_| Error::::ConversionError)?; @@ -235,11 +225,11 @@ impl Pallet { let pool_info = Self::get_bancor_pool(token_id).ok_or(Error::::BancorPoolNotExist)?; // Only if token_ceiling is not zero, then exchangers can exchange vstokens for tokens. - ensure!(pool_info.token_ceiling > Zero::zero(), Error::::TokenSupplyNotEnought); + ensure!(pool_info.token_ceiling > Zero::zero(), Error::::TokenSupplyNotEnough); let (token_supply, vstoken_supply) = ( - pool_info.token_base_supply + pool_info.token_pool, - pool_info.vstoken_base_supply + pool_info.vstoken_pool, + pool_info.token_base_supply.saturating_add(pool_info.token_pool), + pool_info.vstoken_base_supply.saturating_add(pool_info.vstoken_pool), ); ensure!(vstoken_supply > Zero::zero(), Error::::AmountNotGreaterThanZero); @@ -256,13 +246,13 @@ impl Pallet { let temp: u128 = token_supply.saturated_into(); BigUint::from(temp) }; - let token_supply_squre = + let token_supply_square = token_supply.checked_mul(&token_supply).ok_or(Error::::CalculationOverflow)?; - let nominator_lhs = token_supply_squre + let nominator_lhs = token_supply_square .checked_mul(&vstoken_supply) .ok_or(Error::::CalculationOverflow)?; - let nominator_rhs = token_supply_squre + let nominator_rhs = token_supply_square .checked_mul(&vstoken_amount) .ok_or(Error::::CalculationOverflow)?; let nominator = nominator_lhs @@ -271,16 +261,16 @@ impl Pallet { let inside = nominator.checked_div(&vstoken_supply).ok_or(Error::::CalculationOverflow)?; - let squre_root = inside.nth_root(2); + let square_root = inside.nth_root(2); let result = - squre_root.checked_sub(&token_supply).ok_or(Error::::CalculationOverflow)?; + square_root.checked_sub(&token_supply).ok_or(Error::::CalculationOverflow)?; let result_convert: u128 = u128::from_str_radix(&result.to_str_radix(10), 10) .map_err(|_| Error::::ConversionError)?; let price = BalanceOf::::saturated_from(result_convert); - // We can not exchage for more than that the the pool has - ensure!(price <= pool_info.token_ceiling, Error::::TokenSupplyNotEnought); + // We can not exchange for more than that the the pool has + ensure!(price <= pool_info.token_ceiling, Error::::TokenSupplyNotEnough); Ok(price) } @@ -297,17 +287,18 @@ impl Pallet { ensure!(token_amount > Zero::zero(), Error::::AmountNotGreaterThanZero); let pool_info = Self::get_bancor_pool(token_id).ok_or(Error::::BancorPoolNotExist)?; - ensure!(pool_info.vstoken_pool > Zero::zero(), Error::::VSTokenSupplyNotEnought); + ensure!(pool_info.vstoken_pool > Zero::zero(), Error::::VSTokenSupplyNotEnough); let (token_supply, vstoken_supply) = ( - pool_info.token_base_supply + pool_info.token_pool, - pool_info.vstoken_base_supply + pool_info.vstoken_pool, + pool_info.token_base_supply.saturating_add(pool_info.token_pool), + pool_info.vstoken_base_supply.saturating_add(pool_info.vstoken_pool), ); // Since token_amount will be deducted from the total token_supply, token_amount should be - // less than or eqaul to token_supply. - ensure!(token_amount <= token_supply, Error::::TokenSupplyNotEnought); - let mid_item: Perbill = PerThing::from_rational(token_supply - token_amount, token_supply); + // less than or equal to token_supply. + ensure!(token_amount <= token_supply, Error::::TokenSupplyNotEnough); + let mid_item: Perbill = + PerThing::from_rational(token_supply.saturating_sub(token_amount), token_supply); let square_item: Perbill = mid_item.square(); // Destruct the nominator from permill and divide the result by the denominator of a @@ -317,8 +308,8 @@ impl Pallet { let price = rhs_nominator.saturating_mul(vstoken_supply) / BalanceOf::::saturated_from(BILLION); - // We can not exchage for more than that the the pool has - ensure!(price <= pool_info.vstoken_pool, Error::::VSTokenSupplyNotEnought); + // We can not exchange for more than that the the pool has + ensure!(price <= pool_info.vstoken_pool, Error::::VSTokenSupplyNotEnough); Ok(price) } @@ -331,8 +322,8 @@ impl Pallet { ) -> Result<(BalanceOf, BalanceOf), Error> { let pool_info = Self::get_bancor_pool(currency_id).ok_or(Error::::BancorPoolNotExist)?; let (token_supply, vstoken_supply) = ( - pool_info.token_base_supply + pool_info.token_pool, - pool_info.vstoken_base_supply + pool_info.vstoken_pool, + pool_info.token_base_supply.saturating_add(pool_info.token_pool), + pool_info.vstoken_base_supply.saturating_add(pool_info.vstoken_pool), ); Ok((token_supply, BalanceOf::::saturated_from(2u128).saturating_mul(vstoken_supply))) @@ -344,8 +335,8 @@ impl Pallet { ) -> Result<(BalanceOf, BalanceOf), Error> { let pool_info = Self::get_bancor_pool(currency_id).ok_or(Error::::BancorPoolNotExist)?; let (token_supply, vstoken_supply) = ( - pool_info.token_base_supply + pool_info.token_pool, - pool_info.vstoken_base_supply + pool_info.vstoken_pool, + pool_info.token_base_supply.saturating_add(pool_info.token_pool), + pool_info.vstoken_base_supply.saturating_add(pool_info.vstoken_pool), ); Ok((BalanceOf::::saturated_from(2u128).saturating_mul(vstoken_supply), token_supply)) @@ -377,13 +368,10 @@ impl Pallet { BancorPools::::mutate(currency_id, |pool| -> Result<(), Error> { match pool { Some(pool_info) => { - ensure!( - pool_info.token_pool >= token_amount, - Error::::TokenSupplyNotEnought - ); + ensure!(pool_info.token_pool >= token_amount, Error::::TokenSupplyNotEnough); ensure!( pool_info.vstoken_pool >= vstoken_amount, - Error::::VSTokenSupplyNotEnought + Error::::VSTokenSupplyNotEnough ); pool_info.token_pool = pool_info.token_pool.saturating_sub(token_amount); pool_info.vstoken_pool = pool_info.vstoken_pool.saturating_sub(vstoken_amount); @@ -406,7 +394,7 @@ impl Pallet { Some(pool_info) => { ensure!( pool_info.token_ceiling >= token_amount, - Error::::TokenSupplyNotEnought + Error::::TokenSupplyNotEnough ); pool_info.token_ceiling = pool_info.token_ceiling.saturating_sub(token_amount); pool_info.token_pool = pool_info.token_pool.saturating_add(token_amount); @@ -433,15 +421,15 @@ impl BancorHandler> for Pallet { let amount_kept: BalanceOf; // if vstoken price is lower than 0.75 token - if BalanceOf::::saturated_from(100u128).saturating_mul(nominator) <= - denominator.saturating_mul(T::InterventionPercentage::get()) + if T::InterventionPercentage::get().saturating_reciprocal_mul_floor(nominator) <= + denominator { amount_kept = token_amount / BalanceOf::::saturated_from(2u128); } else { amount_kept = token_amount; } - let sell_amount = token_amount - amount_kept; + let sell_amount = token_amount.saturating_sub(amount_kept); // deal with ceiling variable if amount_kept != Zero::zero() { diff --git a/pallets/bancor/src/mock.rs b/pallets/bancor/src/mock.rs index 092c3ee271..17564b9af1 100644 --- a/pallets/bancor/src/mock.rs +++ b/pallets/bancor/src/mock.rs @@ -19,12 +19,12 @@ #![cfg(test)] use frame_support::{construct_runtime, parameter_types, traits::GenesisBuild}; -use node_primitives::{Balance, CurrencyId, TokenSymbol}; +pub use node_primitives::{Balance, CurrencyId, TokenSymbol}; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, - AccountId32, + AccountId32, Percent, }; use crate as bancor; @@ -108,13 +108,14 @@ impl orml_tokens::Config for Test { } parameter_types! { - pub const InterventionPercentage: u128 = 75; + pub const InterventionPercentage: Percent = Percent::from_percent(75); } impl bancor::Config for Test { type Event = Event; type InterventionPercentage = InterventionPercentage; type MultiCurrenciesHandler = Assets; + type WeightInfo = (); } pub struct ExtBuilder { diff --git a/pallets/bancor/src/tests.rs b/pallets/bancor/src/tests.rs index 4f5fa2ff8d..cdc02e8676 100644 --- a/pallets/bancor/src/tests.rs +++ b/pallets/bancor/src/tests.rs @@ -55,7 +55,7 @@ fn exchange_for_token_should_work() { // the pool has no real DOT assert_noop!( Bancor::exchange_for_token(Origin::signed(ALICE), DOT, 50, 48), - Error::::TokenSupplyNotEnought + Error::::TokenSupplyNotEnough ); let updated_pool = BancorPool { @@ -131,7 +131,7 @@ fn exchange_for_vstoken_should_work() { // the pool has no real VSDOT assert_noop!( Bancor::exchange_for_vstoken(Origin::signed(ALICE), DOT, 50, 48), - Error::::VSTokenSupplyNotEnought + Error::::VSTokenSupplyNotEnough ); let updated_pool = BancorPool { diff --git a/runtime/asgard/src/weights/pallet_vtoken_mint.rs b/pallets/bancor/src/weights.rs similarity index 51% rename from runtime/asgard/src/weights/pallet_vtoken_mint.rs rename to pallets/bancor/src/weights.rs index 87109afc60..e82e85c2a7 100644 --- a/runtime/asgard/src/weights/pallet_vtoken_mint.rs +++ b/pallets/bancor/src/weights.rs @@ -16,21 +16,40 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 +#![allow(unused_parens)] +#![allow(unused_imports)] -use frame_support::{traits::Get, weights::Weight}; +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; use sp_std::marker::PhantomData; -pub struct WeightInfo(PhantomData); -impl bifrost_vtoken_mint::WeightInfo for WeightInfo { - fn to_vtoken() -> Weight { - let referer_weight = 1000; - let db = T::DbWeight::get(); - db.reads_writes(1, 1).saturating_add(referer_weight) // memo length + +/// Weight functions needed for the pallet. +pub trait WeightInfo { + fn exchange_for_token() -> Weight; + fn exchange_for_vstoken() -> Weight; +} + +/// Weights for the pallet using the Bifrost node and recommended hardware. +pub struct BifrostWeight(PhantomData); +impl WeightInfo for BifrostWeight { + fn exchange_for_token() -> Weight { + (50_000_000 as Weight) + } + + fn exchange_for_vstoken() -> Weight { + (50_000_000 as Weight) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn exchange_for_token() -> Weight { + (50_000_000 as Weight) } - fn to_token() -> Weight { - (39_603_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + fn exchange_for_vstoken() -> Weight { + (50_000_000 as Weight) } } diff --git a/pallets/charge-transaction-fee/src/lib.rs b/pallets/charge-transaction-fee/src/lib.rs index 7d51cb8483..a62891a8dc 100644 --- a/pallets/charge-transaction-fee/src/lib.rs +++ b/pallets/charge-transaction-fee/src/lib.rs @@ -49,7 +49,7 @@ mod default_weight; mod mock; mod tests; -type CurrencyIdOf = <::CurrenciesHandler as MultiCurrency< +type CurrencyIdOf = <::MultiCurrency as MultiCurrency< ::AccountId, >>::CurrencyId; @@ -77,7 +77,7 @@ pub mod pallet { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; /// Handler for both NativeCurrency and MultiCurrency - type CurrenciesHandler: MultiCurrency< + type MultiCurrency: MultiCurrency< Self::AccountId, CurrencyId = CurrencyId, Balance = Self::Balance, @@ -86,7 +86,7 @@ pub mod pallet { type Currency: Currency + ReservableCurrency; /// Handler for the unbalanced decrease type OnUnbalanced: OnUnbalanced>; - type ZenlinkOperator: ExportZenlink; + type DexOperator: ExportZenlink; #[pallet::constant] type NativeCurrencyId: Get; @@ -217,7 +217,7 @@ impl Pallet { } else { // If it is other assets let native_balance = - T::CurrenciesHandler::free_balance(T::NativeCurrencyId::get(), who); + T::MultiCurrency::free_balance(T::NativeCurrencyId::get(), who); // If native token balance is below existential deposit requirement, // go exchange fee + existential deposit. Else to exchange fee amount. @@ -228,7 +228,7 @@ impl Pallet { amount_out = (fee + existential_deposit).saturated_into(); } - let asset_balance = T::CurrenciesHandler::free_balance(currency_id, who); + let asset_balance = T::MultiCurrency::free_balance(currency_id, who); let asset_id: AssetId = AssetId::try_from(currency_id).map_err(|_| Error::::ConversionError)?; @@ -236,10 +236,10 @@ impl Pallet { let amount_in_max: AssetBalance = asset_balance.saturated_into(); // query for amount in - let amounts = T::ZenlinkOperator::get_amount_in_by_path(amount_out, &path) - .map_or(vec![0], |v| v); + let amounts = + T::DexOperator::get_amount_in_by_path(amount_out, &path).map_or(vec![0], |v| v); - if T::ZenlinkOperator::inner_swap_assets_for_exact_assets( + if T::DexOperator::inner_swap_assets_for_exact_assets( &who, amount_out, amount_in_max, @@ -288,12 +288,12 @@ impl Pallet { } } else { // If it is other assets - let asset_balance = T::CurrenciesHandler::total_balance(currency_id, who); + let asset_balance = T::MultiCurrency::total_balance(currency_id, who); let token_asset_id: AssetId = AssetId::try_from(currency_id) .map_err(|_| DispatchError::Other("Conversion Error"))?; let path = vec![native_asset_id.clone(), token_asset_id]; - let amount_vec = T::ZenlinkOperator::get_amount_in_by_path(amount_out, &path)?; + let amount_vec = T::DexOperator::get_amount_in_by_path(amount_out, &path)?; let amount_in = amount_vec[0]; let amount_in_balance = amount_in.saturated_into(); diff --git a/pallets/charge-transaction-fee/src/mock.rs b/pallets/charge-transaction-fee/src/mock.rs index bb5ab53216..42556a383d 100644 --- a/pallets/charge-transaction-fee/src/mock.rs +++ b/pallets/charge-transaction-fee/src/mock.rs @@ -170,13 +170,13 @@ parameter_types! { impl crate::Config for Test { type Balance = u64; - type CurrenciesHandler = Currencies; type Currency = Balances; + type DexOperator = ZenlinkProtocol; type Event = Event; + type MultiCurrency = Currencies; type NativeCurrencyId = NativeCurrencyId; type OnUnbalanced = (); type WeightInfo = (); - type ZenlinkOperator = ZenlinkProtocol; } parameter_types! { diff --git a/pallets/minter-reward/Cargo.toml b/pallets/minter-reward/Cargo.toml index 72ffe13539..cbffa1b91e 100644 --- a/pallets/minter-reward/Cargo.toml +++ b/pallets/minter-reward/Cargo.toml @@ -20,6 +20,7 @@ orml-currencies = { version = "0.4.1-dev" } pallet-balances = { version = "3.0.0" } sp-io = { version = "3.0.0" } sp-core = { version = "3.0.0" } +bifrost-vtoken-mint = { path = "../vtoken-mint", default-features = false } [features] default = ["std"] @@ -31,5 +32,5 @@ std = [ "frame-system/std", "orml-traits/std", "sp-runtime/std", - "zenlink-protocol/std", + "zenlink-protocol/std" ] \ No newline at end of file diff --git a/pallets/minter-reward/src/lib.rs b/pallets/minter-reward/src/lib.rs index efb8a49361..a1e204c360 100644 --- a/pallets/minter-reward/src/lib.rs +++ b/pallets/minter-reward/src/lib.rs @@ -18,20 +18,19 @@ #![cfg_attr(not(feature = "std"), no_std)] -use core::marker::PhantomData; +use core::{convert::TryFrom, marker::PhantomData}; use fixed::{types::extra::U0, FixedU128}; #[cfg(feature = "std")] pub use frame_support::traits::GenesisBuild; use frame_support::{ pallet_prelude::{ - Blake2_128Concat, DispatchResultWithPostInfo, IsType, StorageDoubleMap, StorageMap, - StorageValue, ValueQuery, + Blake2_128Concat, IsType, StorageDoubleMap, StorageMap, StorageValue, ValueQuery, }, traits::{Get, Hooks}, - PalletId as SystemPalletId, Parameter, + Parameter, }; -use frame_system::pallet_prelude::{ensure_signed, BlockNumberFor, OriginFor}; +use frame_system::pallet_prelude::BlockNumberFor; use node_primitives::{CurrencyId, MinterRewardExt, TokenSymbol}; use orml_traits::{ currency::TransferAll, MultiCurrency, MultiCurrencyExtended, MultiLockableCurrency, @@ -71,24 +70,24 @@ pub mod pallet { /// Event type Event: From> + IsType<::Event>; - /// Two year as a round, 600 * 24 * 365 * 2 + /// Two year as a cycle, 600 * 24 * 365 * 2, which results in reward being cut half #[pallet::constant] - type TwoYear: Get>; + type HalvingCycle: Get>; /// Reward period, normally it's 50 blocks after. #[pallet::constant] - type RewardPeriod: Get>; + type RewardWindow: Get>; /// Allow maximum blocks can be extended. #[pallet::constant] type MaximumExtendedPeriod: Get>; - /// Get price from swap module to compare maximumm vtoken minted - type ZenlinkOperator: ExportZenlink; - - /// Identifier for adjusting weight + /// stable currency id currently used in the chain #[pallet::constant] - type SystemPalletId: Get; + type StableCurrencyId: Get; + + /// Get price from swap module to compare maximum vtoken minted + type DexOperator: ExportZenlink; type ShareWeight: Member + Parameter @@ -102,21 +101,21 @@ pub mod pallet { /// How much BNC will be issued to minters each block after. #[pallet::storage] - #[pallet::getter(fn reward_by_one_block)] - pub(crate) type BNCRewardByOneBlock = StorageValue<_, BalanceOf, ValueQuery>; + #[pallet::getter(fn reward_per_block)] + pub(crate) type RewardPerBlock = StorageValue<_, BalanceOf, ValueQuery>; - /// Ieally, BNC reward will be issued after each 50 blocks. + /// Ideally, BNC reward will be issued after each 50 blocks. #[pallet::storage] #[pallet::getter(fn current_round_start_at)] pub type CurrentRoundStartAt = StorageValue<_, BlockNumberFor, ValueQuery>; // BNC reward will be issued by weight calculation. #[pallet::storage] - #[pallet::getter(fn weight)] - pub type Weights = + #[pallet::getter(fn currency_weight)] + pub type CurrencyWeights = StorageMap<_, Blake2_128Concat, CurrencyIdOf, T::ShareWeight, ValueQuery>; - // Total vtoken minted while in one Period + // Total vtoken minted within one round #[pallet::storage] #[pallet::getter(fn total_vtoken_minted)] pub type TotalVtokenMinted = @@ -140,20 +139,20 @@ pub mod pallet { pub(crate) type MaximumVtokenMinted = StorageValue< _, // (when, amount, currency _id, extended) - (BlockNumberFor, BalanceOf, CurrencyIdOf, IsExtended), + (BlockNumberFor, BalanceOf, CurrencyIdOf), ValueQuery, >; - /// Record a user how much bnc s/he reveives. + /// Record a user how much bnc s/he receives. #[pallet::storage] - #[pallet::getter(fn user_bnc_reward)] - pub(crate) type UserBNCReward = + #[pallet::getter(fn user_reward)] + pub(crate) type UserReward = StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>; /// Record maximum vtoken value is minted and when minted #[pallet::storage] #[pallet::getter(fn current_round)] - pub(crate) type CurrentRound = StorageValue<_, u8, ValueQuery>; + pub(crate) type CurrentCycle = StorageValue<_, u8, ValueQuery>; #[pallet::event] #[pallet::metadata(BalanceOf = "Balance", CurrencyIdOf = "CurrencyId")] @@ -164,111 +163,73 @@ pub mod pallet { pub struct Pallet(PhantomData); /// No call in this pallet. #[pallet::call] - impl Pallet { - #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] - pub fn mint( - origin: OriginFor, - currency_id: CurrencyIdOf, - #[pallet::compact] vtoken_amount: BalanceOf, - ) -> DispatchResultWithPostInfo { - let minter = ensure_signed(origin)?; - - let current_block = >::block_number(); - - Self::reward_minted_vtoken(&minter, currency_id, vtoken_amount, current_block)?; - - Ok(().into()) - } - } + impl Pallet {} #[pallet::error] pub enum Error { /// There's no price at all. FailToGetSwapPrice, + // AssetId conversion error. + ConversionError, + // Calculation overflow. + CalculationOverflow, } #[pallet::hooks] impl Hooks> for Pallet { fn on_finalize(n: BlockNumberFor) { // reach two year - if n % T::TwoYear::get() == Zero::zero() && n > Zero::zero() { + if n % T::HalvingCycle::get() == Zero::zero() && n > Zero::zero() { // Change round index - CurrentRound::::mutate(|round| { + CurrentCycle::::mutate(|round| { *round += 1u8; }); // cut off half reward next round - BNCRewardByOneBlock::::mutate(|reward| { + RewardPerBlock::::mutate(|reward| { *reward /= BalanceOf::::from(2u32); }); } - // if extended, - // check BNC should be issued or not - // check reaching the period or not + let started_block_num = CurrentRoundStartAt::::get(); - if n - started_block_num >= T::RewardPeriod::get() && started_block_num > Zero::zero() { - // mint period is not extended. - let (last_max_minted_block, _current_max_minted, _last_currency_id, is_extended) = - MaximumVtokenMinted::::get(); - // not extended - if !is_extended { - // issue BNC reward to minters - let period = - BalanceOf::::from(T::RewardPeriod::get().saturated_into::()); - // let period: BalanceOf = T::RewardPeriod::get().unique_saturated_into(); - let toal_reward = period * BNCRewardByOneBlock::::get(); - Self::issue_bnc_reward(toal_reward); - - // after issued reward, need to clean this round data - let _ = MaximumVtokenMinted::::kill(); - CurrentRoundStartAt::::put(BlockNumberFor::::from(0u32)); - let _ = Minter::::remove_all(None); - let _ = TotalVtokenMinted::::remove_all(None); - } else { - // mint period is extended - // two senario need to consider - if n - last_max_minted_block >= T::RewardPeriod::get() { - let period = - BalanceOf::::from((n - started_block_num).saturated_into::()); - let toal_reward = period * BNCRewardByOneBlock::::get(); - Self::issue_bnc_reward(toal_reward); - // after issued reward, need to clean this round data - let _ = MaximumVtokenMinted::::kill(); - CurrentRoundStartAt::::put(BlockNumberFor::::from(0u32)); - let _ = Minter::::remove_all(None); - let _ = TotalVtokenMinted::::remove_all(None); - } - - let max_extended_period = T::MaximumExtendedPeriod::get(); - // reaching the MaximumExtendedPeriod, must issue BNC reward. - if n - started_block_num >= max_extended_period { - let period = - BalanceOf::::from(max_extended_period.saturated_into::()); - let toal_reward = period * BNCRewardByOneBlock::::get(); - Self::issue_bnc_reward(toal_reward); - // after issued reward, need to clean this round data - let _ = MaximumVtokenMinted::::kill(); - CurrentRoundStartAt::::put(BlockNumberFor::::from(0u32)); - let _ = Minter::::remove_all(None); - let _ = TotalVtokenMinted::::remove_all(None); - } - } + let max_extended_period = T::MaximumExtendedPeriod::get(); + let (last_max_minted_block, _current_max_minted, _last_currency_id) = + MaximumVtokenMinted::::get(); + + let last_block_diff = n.saturating_sub(last_max_minted_block); + + if (last_block_diff >= T::RewardWindow::get() && started_block_num > Zero::zero()) || + (last_block_diff < T::RewardWindow::get() && + last_block_diff >= max_extended_period && + started_block_num > Zero::zero()) + { + let start_block_diff = n.saturating_sub(started_block_num); + let period = BalanceOf::::from(start_block_diff.saturated_into::()); + + let total_reward = period.saturating_mul(RewardPerBlock::::get()); + + Self::issue_bnc_reward(total_reward); + // after issued reward, need to clean this round data + let _ = MaximumVtokenMinted::::kill(); + CurrentRoundStartAt::::put(BlockNumberFor::::from(0u32)); + let _ = Minter::::remove_all(None); + let _ = TotalVtokenMinted::::remove_all(None); } } } #[pallet::genesis_config] pub struct GenesisConfig { - pub wegiths: Vec<(CurrencyIdOf, T::ShareWeight)>, - pub reward_by_one_block: BalanceOf, - pub round_index: u8, + pub currency_weights: Vec<(CurrencyIdOf, T::ShareWeight)>, + pub reward_per_block: BalanceOf, + pub cycle_index: u8, } #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> GenesisConfig { Self { - wegiths: Default::default(), - reward_by_one_block: Default::default(), - round_index: Default::default(), + currency_weights: Default::default(), + reward_per_block: Default::default(), + cycle_index: Default::default(), } } } @@ -276,12 +237,12 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - for (currency_id, weight) in self.wegiths.iter() { - Weights::::insert(currency_id, weight); + for (currency_id, weight) in self.currency_weights.iter() { + CurrencyWeights::::insert(currency_id, weight); } - CurrentRound::::put(self.round_index); - BNCRewardByOneBlock::::put(self.reward_by_one_block); + CurrentCycle::::put(self.cycle_index); + RewardPerBlock::::put(self.reward_per_block); } } @@ -292,19 +253,11 @@ pub mod pallet { block_num: BlockNumberFor, ) -> Result<(), Error> { let _current_block = >::block_number(); - // let ausd_amount = get_ausd_amount_by_zenlink(minted_vtoken)?; - let (_last_block, current_max_minted, _last_currency_id, is_extended) = + let (_last_block, current_max_minted, _last_currency_id) = MaximumVtokenMinted::::get(); if ausd_amount > current_max_minted { MaximumVtokenMinted::::mutate(|max_minted| { - if block_num.saturating_sub(CurrentRoundStartAt::::get()) >= - T::RewardPeriod::get() - { - if !is_extended { - max_minted.3 = true; - } - } max_minted.0 = block_num; max_minted.1 = ausd_amount; max_minted.2 = currency_id; @@ -317,30 +270,29 @@ pub mod pallet { pub fn issue_bnc_reward(bnc_reward: BalanceOf) { let total_weight: BalanceOf = { let mut total: T::ShareWeight = Zero::zero(); - for (_, _weight) in Weights::::iter() { + for (_, _weight) in CurrencyWeights::::iter() { total = total.saturating_add(_weight); } total.into() }; for (minter, currency_id, vtoken_amount) in Minter::::iter() { - let weight = Weights::::get(¤cy_id); + let weight = CurrencyWeights::::get(¤cy_id); let total_vtoken_mint = TotalVtokenMinted::::get(currency_id); // AUSD let reward = bnc_reward.saturating_mul(weight.into().saturating_mul(vtoken_amount)) / - (total_weight * total_vtoken_mint); + (total_weight.saturating_mul(total_vtoken_mint)); let _ = T::MultiCurrency::deposit( CurrencyId::Native(TokenSymbol::ASG), &minter, reward, ); - // let _ = T::NativeCurrency::deposit(&minter, reward); // Record all BNC rewards the user receives. - if UserBNCReward::::contains_key(&minter) { - UserBNCReward::::mutate(&minter, |balance| { + if UserReward::::contains_key(&minter) { + UserReward::::mutate(&minter, |balance| { *balance = balance.saturating_add(reward); }) } else { - UserBNCReward::::insert(&minter, reward); + UserReward::::insert(&minter, reward); } } } @@ -349,16 +301,14 @@ pub mod pallet { vtoken_amount: BalanceOf, currency_id: CurrencyId, ) -> Result, Error> { - let ausd_amount = T::ZenlinkOperator::get_amount_out_by_path( + let currency_asset_id = + AssetId::try_from(currency_id).map_err(|_| Error::::ConversionError)?; + let stable_asset_id = AssetId::try_from(T::StableCurrencyId::get()) + .map_err(|_| Error::::ConversionError)?; + + let ausd_amount = T::DexOperator::get_amount_out_by_path( vtoken_amount.saturated_into(), - &[ - AssetId { - chain_id: 2001 as u32, - asset_type: 2, - asset_index: *currency_id as u32, - }, - AssetId { chain_id: 2001 as u32, asset_type: 2, asset_index: 2 as u32 }, - ], + &[currency_asset_id, stable_asset_id], ) .map_err(|_| Error::::FailToGetSwapPrice)? .last() @@ -377,19 +327,19 @@ impl MinterRewardExt, CurrencyIdOf, Blo fn reward_minted_vtoken( minter: &T::AccountId, - currency_id: CurrencyId, + vtoken_id: CurrencyId, minted_vtoken: BalanceOf, block_num: BlockNumberFor, ) -> Result<(), Self::Error> { - let ausd_amount = Self::get_ausd_amount_by_zenlink(minted_vtoken, currency_id)?; + let ausd_amount = Self::get_ausd_amount_by_zenlink(minted_vtoken, vtoken_id)?; // Update minter mint how much vtoken - if TotalVtokenMinted::::contains_key(currency_id) { - TotalVtokenMinted::::mutate(currency_id, |total| { + if TotalVtokenMinted::::contains_key(vtoken_id) { + TotalVtokenMinted::::mutate(vtoken_id, |total| { total.saturating_add(ausd_amount.saturated_into()); }); } else { - TotalVtokenMinted::::insert(currency_id, ausd_amount); + TotalVtokenMinted::::insert(vtoken_id, ausd_amount); } // check it is a new round @@ -398,14 +348,14 @@ impl MinterRewardExt, CurrencyIdOf, Blo } // Update minter mint how much vtoken - if Minter::::contains_key(minter, ¤cy_id) { - Minter::::mutate(minter, ¤cy_id, |minted| { + if Minter::::contains_key(minter, &vtoken_id) { + Minter::::mutate(minter, &vtoken_id, |minted| { minted.saturating_add(ausd_amount); }); } else { - Minter::::insert(minter, ¤cy_id, ausd_amount); + Minter::::insert(minter, &vtoken_id, ausd_amount); } - Self::compare_max_vtoken_minted(currency_id, ausd_amount, block_num) + Self::compare_max_vtoken_minted(vtoken_id, ausd_amount, block_num) } } diff --git a/pallets/minter-reward/src/lib1.rs b/pallets/minter-reward/src/lib1.rs deleted file mode 100644 index cfeedff396..0000000000 --- a/pallets/minter-reward/src/lib1.rs +++ /dev/null @@ -1,394 +0,0 @@ -// This file is part of Bifrost. - -// Copyright (C) 2019-2021 Liebi Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -pub use self::pallet::*; - -#[frame_support::pallet] -pub mod pallet { - use core::marker::PhantomData; - use fixed::{types::extra::U0, FixedU128}; - use frame_support::{ - Parameter, debug, - traits::{Get, Hooks, Currency, ReservableCurrency}, - pallet_prelude::{ - Blake2_128Concat, ensure, StorageMap, StorageValue, - ValueQuery, StorageDoubleMap - } - }; - #[cfg(feature = "std")] - pub use frame_support::traits::GenesisBuild; - use frame_system::pallet_prelude::BlockNumberFor; - use node_primitives::{MintTrait, DEXOperations}; - use sp_runtime::traits::{ - AtLeast32Bit, Member, Saturating, Zero, MaybeSerializeDeserialize, UniqueSaturatedInto - }; - - type Fix = FixedU128; - type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The currency trait is going to manpulate balances module. - type Currency: ReservableCurrency; - - /// BNC price half interval amounts - #[pallet::constant] - type PriceHalfBlockInterval: Get; - - /// BNC issue max block number - #[pallet::constant] - type MaxIssueBlockInterval: Get; - - /// Max transaction amounts - #[pallet::constant] - type MaxTxAmount: Get; - - /// BNC pledge base amounts - #[pallet::constant] - type PledgeBaseAmount: Get; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData); - - #[pallet::call] - impl Pallet {} - - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(current_block_number: BlockNumberFor) { - // Get current block generates bnc stimulate - let (record_block_number, mut current_bnc_price) = BncPrice::::get(); - let zero_balance: BalanceOf = Zero::zero(); - // Check bnc price - if current_bnc_price.eq(&zero_balance) { return } - - if current_block_number.saturating_sub(record_block_number) - .eq(&BlockNumberFor::::from(T::PriceHalfBlockInterval::get())) { - BncPrice::::mutate (|(record_block_number, bnc_price)| { - *record_block_number = current_block_number; - *bnc_price /= BalanceOf::::from(2u32); - }); - current_bnc_price = BncPrice::::get().1; - } - - // Bnc stimulate - Self::count_bnc(current_bnc_price); - // Obtain monitor data - let ((previous_block_numer, bnc_mint_amount), max_bnc_mint_amount, tx_amount) - = BncMonitor::::get(); - - // Check issue condition - if current_block_number.saturating_sub(previous_block_numer) - .eq(&BlockNumberFor::::from(T::MaxIssueBlockInterval::get())) - && BncSum::::get().ne(&zero_balance) && max_bnc_mint_amount.ne(&zero_balance) - || tx_amount.ge(&T::MaxTxAmount::get()) - { - // issue bnc - match Self::issue_bnc_by_weight() { - Ok(_) => return, - Err(e) => debug::error!("An error happened while issue bnc : {:?}", e) - } - } - - // Update block_number and max_bnc_mint_amount - if max_bnc_mint_amount.gt(&bnc_mint_amount) { - BncMonitor::::mutate(|(tup, _, _)|{ - tup.0 = current_block_number; - tup.1 = max_bnc_mint_amount; - }); - } - } - } - - #[pallet::error] - pub enum Error { - /// No included referer - MinterNotExist, - /// Bnc total amount is zero - BncAmountNotExist, - /// Vtoken not Exist - AssetScoreNotExist, - /// pledge amount not enough - PledgeAmountNotEnough, - /// Bnc issue fail - DepositBncFailure, - } - - #[pallet::storage] - #[pallet::getter(fn bnc_sum)] - pub(crate) type BncSum = StorageValue<_, BalanceOf, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn bnc_price)] - pub(crate) type BncPrice = StorageValue<_, (BlockNumberFor, BalanceOf), ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn bnc_monitor)] - pub(crate) type BncMonitor = StorageValue< - _, - ((BlockNumberFor, BalanceOf), BalanceOf, u32), - ValueQuery - >; - - #[pallet::storage] - #[pallet::getter(fn bnc_mint)] - pub(crate) type BncMint = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - BalanceOf, - ValueQuery - >; - - #[pallet::storage] - #[pallet::getter(fn vtoken_weight)] - pub(crate) type VtokenWeightScore = StorageMap< - _, - Blake2_128Concat, - T::AssetId, - (BalanceOf, BalanceOf), - ValueQuery - >; - - #[pallet::storage] - #[pallet::getter(fn vtoken_mint)] - pub(crate) type VtokenWeightMint = StorageDoubleMap< - _, - Blake2_128Concat, - T::AssetId, - Blake2_128Concat, - T::AccountId, - BalanceOf, - ValueQuery - >; - - #[pallet::storage] - #[pallet::getter(fn storage_version)] - pub(crate) type StorageVersion = StorageValue< - _, - node_primitives::StorageVersion, - ValueQuery, - >; - - #[pallet::genesis_config] - pub struct GenesisConfig { - pub block_num: BlockNumberFor, - pub bnc_price: BalanceOf, - } - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> GenesisConfig { - GenesisConfig { - block_num: Zero::zero(), - bnc_price: Zero::zero(), - } - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - BncPrice::::put((self.block_num, self.bnc_price)); - } - } - - impl MintTrait, T::AssetId> for Pallet { - type Error = Error; - - // Statistics bnc - fn count_bnc(generate_amount: BalanceOf) { - BncSum::::mutate(|bnc_amount| { - *bnc_amount = bnc_amount.saturating_add(generate_amount); - }); - } - - // Settlement model mint - fn mint_bnc(minter: T::AccountId, mint_amount: BalanceOf) -> Result<(), Self::Error> { - // Judge - if BncMint::::contains_key(&minter) { - BncMint::::mutate(minter, |v| { - *v = v.saturating_add(mint_amount) - }); - } else { - BncMint::::insert(minter, mint_amount); - } - - let (_, max_bnc_amount, _) = BncMonitor::::get(); - if mint_amount.gt(&max_bnc_amount) { - // Update max_bnc_amount - BncMonitor::::mutate(|(_, max_val, _)| { - *max_val = mint_amount; - }) - } - BncMonitor::::mutate(|(_, _, tx_amount)| { - *tx_amount = tx_amount.saturating_add(1); - }); - - Ok(()) - } - - // Settlement model mint - fn issue_bnc() -> Result<(), Self::Error> { - // Check Bnc total amount - let zero_balance: BalanceOf = Zero::zero(); - let zero_block_number: BlockNumberFor= Zero::zero(); - ensure!(BncSum::::get().ne(&zero_balance), Error::::BncAmountNotExist); - let bnc_amount = BncSum::::get(); - // Get total point - let sum: BalanceOf = - BncMint::::iter().fold(zero_balance, |acc, x| acc.saturating_add(x.1)); - // Check minter point - ensure!(sum.ne(&zero_balance), Error::::MinterNotExist); - - // Traverse dispatch BNC reward - for (minter, point) in BncMint::::iter() { - let minter_reward = point.saturating_mul(bnc_amount) / sum; - if minter_reward.ne(&zero_balance) { - ensure!( - T::Currency::deposit_into_existing(&minter, minter_reward).is_ok(), - Error::::DepositBncFailure - ); - } - } - // Reset BncSum - BncSum::::put(zero_balance); - // Clear BncMint - for _ in BncMint::::drain() {}; - // Clear Monitor data - BncMonitor::::put(((zero_block_number, zero_balance), zero_balance, 0u32)); - - Ok(()) - } - - // Currency weight model - fn v_token_score_exists(asset_id: T::AssetId) -> bool { - VtokenWeightScore::::contains_key(&asset_id) - } - - fn init_v_token_score(asset_id: T::AssetId, score: BalanceOf) { - let adjust_score: BalanceOf = Zero::zero(); - VtokenWeightScore::::insert(asset_id, (score, adjust_score)); - } - - fn mint_bnc_by_weight(minter: T::AccountId, mint_amount: BalanceOf, asset_id: T::AssetId) - -> Result<(), Self::Error> - { - ensure!(Self::v_token_score_exists(asset_id), Error::::AssetScoreNotExist); - // Judge - if VtokenWeightMint::::contains_key(&asset_id, &minter) { - VtokenWeightMint::::mutate(&asset_id, &minter, |v| { - *v = v.saturating_add(mint_amount); - }); - } else { - VtokenWeightMint::::insert(asset_id, minter, mint_amount); - } - - // Obtain max_bnc_amount - let (_, max_bnc_amount, _) = BncMonitor::::get(); - if mint_amount.gt(&max_bnc_amount) { - // Update max_bnc_amount - BncMonitor::::mutate(|(_, max_val, _)| { - *max_val = mint_amount; - }) - } - BncMonitor::::mutate(|(_, _, tx_amount)| { - *tx_amount = tx_amount.saturating_add(1); - }); - - Ok(()) - } - - fn issue_bnc_by_weight() -> Result<(), Self::Error> { - // Check Bnc total amount - let zero_balance: BalanceOf = Zero::zero(); - ensure!(BncSum::::get().ne(&zero_balance), Error::::BncAmountNotExist); - let bnc_amount = BncSum::::get(); - let total_score: BalanceOf = VtokenWeightScore::::iter() - .fold(zero_balance, |acc, x| acc.saturating_add(x.1.0).saturating_add(x.1.1)); - - // Traverse - for (asset_id, (base_score, adjust_score)) in VtokenWeightScore::::iter() { - let v_token_reward = base_score.saturating_add(adjust_score) - .saturating_mul(bnc_amount) / total_score; - // Get v_token point - let v_token_point: BalanceOf = VtokenWeightMint::::iter_prefix(&asset_id) - .fold(zero_balance, |acc, x| acc.saturating_add(x.1)); - // Check asset point - if v_token_point.eq(&zero_balance) { continue } - // Traverse dispatch BNC reward - for (minter,point) in VtokenWeightMint::::iter_prefix(asset_id) { - let minter_reward = point.saturating_mul(v_token_reward) / v_token_point; - if minter_reward.ne(&zero_balance) { - ensure!( - T::Currency::deposit_into_existing(&minter, minter_reward).is_ok(), - Error::::DepositBncFailure - ); - } - } - } - - // Reset BncSum - BncSum::::put(zero_balance); - // Clear BncMint - for _ in VtokenWeightMint::::drain() {}; - // Clear Monitor data - let zero_block_number: BlockNumberFor = Zero::zero(); - BncMonitor::::put(((zero_block_number, zero_balance), zero_balance, 0u32)); - - Ok(()) - } - - fn improve_v_token_weight(asset_id: T::AssetId, pledge_amount: BalanceOf) - -> Result<(), Self::Error> - { - let base_amount = BalanceOf::::from(T::PledgeBaseAmount::get()); - ensure!(pledge_amount.gt(&base_amount), Error::::PledgeAmountNotEnough); - // Add weight score - VtokenWeightScore::::mutate(asset_id, |(_, v)| { - if let Some(x) = Fix::from_num::(pledge_amount.saturating_sub(base_amount) - .unique_saturated_into()).checked_int_log2() - { - *v = v.saturating_add(BalanceOf::::from(x as u32)); - } - }); - - Ok(()) - } - - fn withdraw_v_token_pledge(asset_id: T::AssetId, pledge_amount: BalanceOf) - -> Result<(), Self::Error> - { - let base_amount = BalanceOf::::from(T::PledgeBaseAmount::get()); - ensure!(pledge_amount.gt(&base_amount), Error::::PledgeAmountNotEnough); - // Reduce weight score - VtokenWeightScore::::mutate(asset_id, |(_, v)| { - if let Some(x) = Fix::from_num::(pledge_amount.saturating_sub(base_amount) - .unique_saturated_into()).checked_int_log2() - { - *v = v.saturating_sub(BalanceOf::::from(x as u32)); - } - }); - - Ok(()) - } - } -} diff --git a/pallets/minter-reward/src/mock.rs b/pallets/minter-reward/src/mock.rs index bb36e33e15..6dc17ab715 100644 --- a/pallets/minter-reward/src/mock.rs +++ b/pallets/minter-reward/src/mock.rs @@ -67,6 +67,7 @@ frame_support::construct_runtime!( PalletBalances: pallet_balances::{Pallet, Call, Storage, Config, Event}, MinterReward: bifrost_minter_reward::{Pallet, Call, Storage, Event, Config}, ZenlinkProtocol: zenlink_protocol::{Pallet, Call, Storage, Event}, + VtokenMint: bifrost_vtoken_mint::{Pallet, Call, Storage, Event}, } ); @@ -149,18 +150,18 @@ impl orml_tokens::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const TwoYear: u32 = 40; - pub const RewardPeriod: u32 = 10; - pub const MaximumExtendedPeriod: u32 = 20; - pub const ShareWeightPalletId: PalletId = PalletId(*b"weight "); +impl bifrost_vtoken_mint::Config for Runtime { + type Event = Event; + type MinterReward = MinterReward; + type MultiCurrency = Currencies; + type WeightInfo = (); } // Zenlink runtime implementation parameter_types! { pub const ZenlinkPalletId: PalletId = PalletId(*b"/zenlink"); pub const GetExchangeFee: (u32, u32) = (3, 1000); // 0.3% - // pub const SelfParaId: ParaId = ParaId{0: 2001}; + pub const SelfParaId: u32 = 2001; } impl zenlink_protocol::Config for Runtime { @@ -170,7 +171,7 @@ impl zenlink_protocol::Config for Runtime { type MultiAssetsHandler = MultiAssets; type PalletId = ZenlinkPalletId; // type SelfParaId = SelfParaId; - type SelfParaId = (); + type SelfParaId = SelfParaId; type TargetChains = (); type XcmExecutor = (); } @@ -239,15 +240,22 @@ where // zenlink runtime ends +parameter_types! { + pub const HalvingCycle: u32 = 60; + pub const RewardWindow: u32 = 10; + pub const MaximumExtendedPeriod: u32 = 20; + pub const StableCurrencyId: CurrencyId = CurrencyId::Stable(TokenSymbol::AUSD); +} + impl crate::Config for Runtime { + type DexOperator = ZenlinkProtocol; type Event = Event; + type HalvingCycle = HalvingCycle; type MaximumExtendedPeriod = MaximumExtendedPeriod; type MultiCurrency = Currencies; - type RewardPeriod = RewardPeriod; + type RewardWindow = RewardWindow; type ShareWeight = Balance; - type SystemPalletId = ShareWeightPalletId; - type TwoYear = TwoYear; - type ZenlinkOperator = ZenlinkProtocol; + type StableCurrencyId = StableCurrencyId; } pub struct ExtBuilder { @@ -266,7 +274,7 @@ impl ExtBuilder { self } - pub fn ten_thousand_for_alice(self) -> Self { + pub fn ten_thousand_for_alice_n_bob(self) -> Self { self.balances(vec![ (ALICE, BNC, 100000), (ALICE, AUSD, 10000), @@ -274,6 +282,12 @@ impl ExtBuilder { (ALICE, vKSM, 10000), (ALICE, DOT, 10000), (ALICE, KSM, 10000), + (BOB, BNC, 100000), + (BOB, AUSD, 10000), + (BOB, vDOT, 10000), + (BOB, vKSM, 10000), + (BOB, DOT, 10000), + (BOB, KSM, 10000), ]) } @@ -319,13 +333,13 @@ impl ExtBuilder { .unwrap(); crate::GenesisConfig:: { - wegiths: vec![ + currency_weights: vec![ (CurrencyId::Token(TokenSymbol::DOT), 1 * 1), (CurrencyId::Token(TokenSymbol::ETH), 1 * 1), (CurrencyId::Token(TokenSymbol::KSM), 1 * 3), ], - reward_by_one_block: 300, - round_index: 1, + reward_per_block: 300, + cycle_index: 1, } .assimilate_storage(&mut t) .unwrap(); diff --git a/pallets/minter-reward/src/tests.rs b/pallets/minter-reward/src/tests.rs index 1ca49463b2..0e93950988 100644 --- a/pallets/minter-reward/src/tests.rs +++ b/pallets/minter-reward/src/tests.rs @@ -40,11 +40,11 @@ pub(crate) fn run_to_block(n: u64) { // The following test is ignored due to some bugs on zenlink. It can be reopened after the bug is // fixed.frame_system The functionality has already been tested. #[test] -#[ignore] fn minter_reward_should_work() { - ExtBuilder::default().ten_thousand_for_alice().build().execute_with(|| { + ExtBuilder::default().ten_thousand_for_alice_n_bob().build().execute_with(|| { run_to_block(2); + let to_sell_dot = 20; let to_sell_vdot = 20; // let to_sell_ksm = 20; @@ -122,18 +122,43 @@ fn minter_reward_should_work() { deadline )); - assert_ok!(MinterReward::mint(Origin::signed(ALICE), DOT, to_sell_vdot)); - // assert_eq!(MinterReward::current_round_start_at(), 2); + // set currency staking lock period, 28 blocks for DOT + assert_ok!(VtokenMint::set_token_staking_lock_period( + Origin::root(), + CurrencyId::Token(TokenSymbol::DOT), + 28 + )); + + // add some data to the vtoken mint pools + assert_ok!(VtokenMint::set_vtoken_pool(Origin::root(), DOT, 10000, 10000)); + assert_ok!(VtokenMint::mint(Origin::signed(ALICE), vDOT, to_sell_dot)); + + run_to_block(3); + assert_eq!(MinterReward::current_round_start_at(), 2); run_to_block(10); - // assert_eq!(MinterReward::current_round(), 3); - // assert_eq!(MinterReward::reward_by_one_block(), 75); - dbg!(MinterReward::maximum_vtoken_minted()); + assert_eq!(MinterReward::reward_per_block(), 300); + assert_eq!( + MinterReward::maximum_vtoken_minted(), + (2, 19, CurrencyId::VToken(TokenSymbol::DOT)) + ); run_to_block(12); - assert_ok!(MinterReward::mint(Origin::signed(BOB), DOT, to_sell_vdot + 40)); - // assert_ok!(MinterReward::mint(Origin::signed(ALICE), DOT, to_sell_vdot + 20)); - run_to_block(23); - // run_to_block(17); + assert_ok!(VtokenMint::mint(Origin::signed(BOB), vDOT, to_sell_vdot + 40)); + assert_eq!( + MinterReward::maximum_vtoken_minted(), + (12, 56, CurrencyId::VToken(TokenSymbol::DOT)) + ); + + run_to_block(41); + assert_eq!(MinterReward::current_round_start_at(), 0); + + run_to_block(42); + assert_ok!(VtokenMint::mint(Origin::signed(ALICE), vDOT, to_sell_vdot + 20)); + assert_eq!(MinterReward::current_round_start_at(), 42); + + // 60 blocks to be a halving reward cycle + run_to_block(61); + assert_eq!(MinterReward::reward_per_block(), 150); }); } diff --git a/pallets/salp/src/mock.rs b/pallets/salp/src/mock.rs index 71330ecb8a..1495d674a7 100644 --- a/pallets/salp/src/mock.rs +++ b/pallets/salp/src/mock.rs @@ -109,13 +109,14 @@ impl orml_tokens::Config for Test { } parameter_types! { - pub const InterventionPercentage: Balance = 75; + pub const InterventionPercentage: Percent = Percent::from_percent(75); } impl bifrost_bancor::Config for Test { type Event = Event; type InterventionPercentage = InterventionPercentage; type MultiCurrenciesHandler = Tokens; + type WeightInfo = (); } // TODO: Impl bifrost_xcm_executor::Config diff --git a/pallets/vtoken-mint/Cargo.toml b/pallets/vtoken-mint/Cargo.toml index 8575aba88f..f58a2b79d7 100644 --- a/pallets/vtoken-mint/Cargo.toml +++ b/pallets/vtoken-mint/Cargo.toml @@ -10,18 +10,20 @@ node-primitives = { path = "../../node/primitives", default-features = false } frame-support = { version = "3.0.0", default-features = false } frame-system = { version = "3.0.0", default-features = false } sp-core = { version = "3.0.0", default-features = false } +sp-std = { version = "3.0.0", default-features = false } sp-runtime = { version = "3.0.0", default-features = false } zenlink-protocol = { version = "*", default-features = false } orml-traits = { version = "0.4.1-dev", default-features = false } +bifrost-minter-reward = { path = "../minter-reward", default-features = false } [dev-dependencies] orml-tokens = { version = "0.4.1-dev" } orml-currencies = { version = "0.4.1-dev" } -bifrost-minter-reward = { path = "../minter-reward" } + pallet-balances = "3.0.0" -pallet-randomness-collective-flip = "3.0.0" sp-core = "3.0.0" sp-io = "3.0.0" +sp-std = "3.0.0" [features] default = ["std"] @@ -31,8 +33,10 @@ std = [ "frame-support/std", "frame-system/std", "sp-core/std", + "sp-std/std", "sp-runtime/std", "orml-traits/std", "zenlink-protocol/std", "orml-traits/std", + "bifrost-minter-reward/std" ] diff --git a/pallets/vtoken-mint/src/lib.rs b/pallets/vtoken-mint/src/lib.rs index f8648d8df0..4e2b37a946 100644 --- a/pallets/vtoken-mint/src/lib.rs +++ b/pallets/vtoken-mint/src/lib.rs @@ -19,31 +19,33 @@ extern crate alloc; -use alloc::{collections::btree_map::BTreeMap, vec::Vec}; +use alloc::vec::Vec; use core::marker::PhantomData; use frame_support::{ pallet_prelude::*, - traits::{Get, Hooks, IsType, Randomness}, - transactional, PalletId, + traits::{Hooks, IsType}, + transactional, }; use frame_system::{ ensure_root, ensure_signed, pallet_prelude::{BlockNumberFor, OriginFor}, }; -use node_primitives::{CurrencyId, CurrencyIdExt, MinterRewardExt, TokenSymbol, VtokenMintExt}; +use node_primitives::{CurrencyId, CurrencyIdExt, MinterRewardExt, VtokenMintExt}; use orml_traits::{ currency::TransferAll, MultiCurrency, MultiCurrencyExtended, MultiLockableCurrency, MultiReservableCurrency, }; pub use pallet::*; use sp_runtime::{ - traits::{Saturating, Zero}, - DispatchResult, Permill, + traits::{CheckedSub, Saturating, Zero}, + DispatchResult, }; +use weights::WeightInfo; mod mock; mod tests; +pub mod weights; type BalanceOf = <::MultiCurrency as MultiCurrency< ::AccountId, @@ -56,21 +58,6 @@ type CurrencyIdOf = <::MultiCurrency as MultiCurrency< pub mod pallet { use super::*; - pub trait WeightInfo { - fn to_vtoken() -> Weight; - fn to_token() -> Weight; - } - - impl WeightInfo for () { - fn to_vtoken() -> Weight { - Default::default() - } - - fn to_token() -> Weight { - Default::default() - } - } - #[pallet::config] pub trait Config: frame_system::Config { /// A handler to manipulate assets module. @@ -82,10 +69,6 @@ pub mod pallet { /// Event type Event: From> + IsType<::Event>; - /// Identifier for the staking lock. - #[pallet::constant] - type PalletId: Get; - /// Record mint reward type MinterReward: MinterRewardExt< Self::AccountId, @@ -96,9 +79,6 @@ pub mod pallet { /// Set default weight. type WeightInfo: WeightInfo; - - /// Random source for determinated yield - type RandomnessSource: Randomness; } /// Total mint pool @@ -107,26 +87,6 @@ pub mod pallet { pub(crate) type MintPool = StorageMap<_, Blake2_128Concat, CurrencyIdOf, BalanceOf, ValueQuery>; - /// Collect referrer, minter => ([(referrer1, 1000), (referrer2, 2000), ...], total_point) - /// total_point = 1000 + 2000 + ... - /// referrer must be unique, so check it unique while a new referrer incoming. - /// and insert the new channel to the - #[pallet::storage] - #[pallet::getter(fn referrer_channels)] - pub(crate) type ReferrerChannels = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - (Vec<(T::AccountId, BalanceOf)>, BalanceOf), - ValueQuery, - >; - - /// Referer channels for all users. - #[pallet::storage] - #[pallet::getter(fn all_referer_channels)] - pub(crate) type AllReferrerChannels = - StorageValue<_, (BTreeMap>, BalanceOf), ValueQuery, ()>; - /// Record when and how much balance user want to redeem. #[pallet::storage] #[pallet::getter(fn redeem_record)] @@ -146,39 +106,12 @@ pub mod pallet { pub(crate) type StakingLockPeriod = StorageMap<_, Blake2_128Concat, CurrencyIdOf, T::BlockNumber, ValueQuery>; - /// List user staking revenue. - #[pallet::storage] - #[pallet::getter(fn user_staking_revenue)] - pub(crate) type UserStakingRevenue = StorageDoubleMap< - _, - Blake2_128Concat, - T::AccountId, - Blake2_128Concat, - TokenSymbol, - BalanceOf, - ValueQuery, - >; - - /// The ROI of each token by every block. - #[pallet::storage] - #[pallet::getter(fn rate_of_interest_each_block)] - pub(crate) type RateOfInterestEachBlock = - StorageMap<_, Blake2_128Concat, CurrencyIdOf, BalanceOf, ValueQuery>; - - /// Yeild rate for each token - #[pallet::storage] - #[pallet::getter(fn yield_rate)] - pub(crate) type YieldRate = - StorageMap<_, Blake2_128Concat, CurrencyIdOf, Permill, ValueQuery>; - #[pallet::event] #[pallet::metadata(BalanceOf = "Balance", CurrencyIdOf = "CurrencyId")] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - UpdateRatePerBlockSuccess, Minted(T::AccountId, CurrencyIdOf, BalanceOf), RedeemStarted(T::AccountId, CurrencyIdOf, BalanceOf, T::BlockNumber), - RedeemedPointsSuccess, UpdateVtokenPoolSuccess, } @@ -194,8 +127,8 @@ pub mod pallet { EmptyVtokenPool, /// The amount of token you want to mint is bigger than the vtoken pool. NotEnoughVtokenPool, - /// User's token still under staking while he want to redeem. - UnderStaking, + /// Calculation Overflow + CalculationOverflow, } #[pallet::pallet] @@ -207,15 +140,39 @@ pub mod pallet { /// /// The dispatch origin for this call must be `Root` by the /// transactor. - #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] + #[pallet::weight(T::WeightInfo::set_token_staking_lock_period())] + #[transactional] + pub fn set_token_staking_lock_period( + origin: OriginFor, + token_id: CurrencyIdOf, + locking_blocks: T::BlockNumber, + ) -> DispatchResult { + ensure_root(origin)?; + ensure!(token_id.is_token(), Error::::NotSupportTokenType); + + if StakingLockPeriod::::contains_key(token_id) { + StakingLockPeriod::::mutate(token_id, |locking_period| { + *locking_period = locking_blocks; + }) + } else { + StakingLockPeriod::::insert(token_id, locking_blocks); + } + + Self::deposit_event(Event::UpdateVtokenPoolSuccess); + + Ok(()) + } + + /// Set staking lock period for a token + #[pallet::weight(T::WeightInfo::set_vtoken_pool())] #[transactional] pub fn set_vtoken_pool( origin: OriginFor, token_id: CurrencyIdOf, #[pallet::compact] new_token_pool: BalanceOf, #[pallet::compact] new_vtoken_pool: BalanceOf, - ) -> DispatchResultWithPostInfo { - ensure_root(origin.clone())?; + ) -> DispatchResult { + ensure_root(origin)?; ensure!(token_id.is_token(), Error::::NotSupportTokenType); let vtoken_id = token_id.to_vtoken().map_err(|_| Error::::NotSupportTokenType)?; @@ -225,20 +182,20 @@ pub mod pallet { Self::deposit_event(Event::UpdateVtokenPoolSuccess); - Ok(().into()) + Ok(()) } /// Mint vtoken. /// /// The dispatch origin for this call must be `Signed` by the /// transactor. - #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] + #[pallet::weight(T::WeightInfo::mint())] #[transactional] pub fn mint( origin: OriginFor, vtoken_id: CurrencyIdOf, #[pallet::compact] token_amount: BalanceOf, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { let minter = ensure_signed(origin)?; ensure!(!token_amount.is_zero(), Error::::BalanceZero); @@ -276,20 +233,20 @@ pub mod pallet { Self::deposit_event(Event::Minted(minter, vtoken_id, vtokens_buy)); - Ok(().into()) + Ok(()) } /// Redeem token. /// /// The dispatch origin for this call must be `Signed` by the /// transactor. - #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] + #[pallet::weight(T::WeightInfo::redeem())] #[transactional] pub fn redeem( origin: OriginFor, token_id: CurrencyIdOf, #[pallet::compact] vtoken_amount: BalanceOf, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { let redeemer = ensure_signed(origin)?; ensure!(!vtoken_amount.is_zero(), Error::::BalanceZero); @@ -301,13 +258,6 @@ pub mod pallet { let vtoken_balances = T::MultiCurrency::free_balance(vtoken_id, &redeemer); ensure!(vtoken_balances >= vtoken_amount, Error::::BalanceLow); - Self::update_redeem_record(token_id, &redeemer, vtoken_amount); - - // Just record which vtoken is minted - if !UserStakingRevenue::::contains_key(&redeemer, *vtoken_id) { - UserStakingRevenue::::insert(&redeemer, *vtoken_id, BalanceOf::::from(0u32)); - } - // Reach the end of staking period, begin to redeem. // Total amount of tokens. let token_pool = Self::get_mint_pool(token_id); @@ -323,12 +273,13 @@ pub mod pallet { // Alter redeemer's balance T::MultiCurrency::withdraw(vtoken_id, &redeemer, vtoken_amount)?; - T::MultiCurrency::deposit(token_id, &redeemer, tokens_redeem)?; // Alter mint pool Self::reduce_mint_pool(token_id, tokens_redeem)?; Self::reduce_mint_pool(vtoken_id, vtoken_amount)?; + Self::update_redeem_record(token_id, &redeemer, tokens_redeem); + let current_block = >::block_number(); Self::deposit_event(Event::RedeemStarted( redeemer, @@ -337,70 +288,19 @@ pub mod pallet { current_block, )); - Ok(().into()) + Ok(()) } } #[pallet::hooks] impl Hooks> for Pallet { fn on_finalize(_block_number: T::BlockNumber) { - // dbg!(_block_number); - // Mock staking reward for pulling up vtoken price - let random_sum = Self::mock_yield_change(); - let fluctuation = Permill::from_percent(3); // +- 3% - for (currency_id, _) in MintPool::::iter() { - // Only inject tokens into token pool - let year_rate = YieldRate::::get(¤cy_id); - if year_rate.is_zero() { - continue; - } - - if currency_id.is_token() { - let bonus = MintPool::::get(¤cy_id); - if bonus == Zero::zero() { - continue; - } - - let one_year_blocks = BalanceOf::::from(365 * 24 * 600u32); - if year_rate.deconstruct() % random_sum > random_sum / 2u32 { - // up to 17.8% or 11.2% - let rate = year_rate.saturating_add(fluctuation) * bonus / one_year_blocks; - let _ = Self::expand_mint_pool(currency_id, rate); - // update revenue for each user having vtoken - Self::record_user_staking_revenue(currency_id, rate); - } else { - // down to 11.8% or 5.2% - let rate = year_rate.saturating_sub(fluctuation) * bonus / one_year_blocks; - let _ = Self::expand_mint_pool(currency_id, rate); - // update revenue for each user having vtoken - Self::record_user_staking_revenue(currency_id, rate); - } - } - } - // Check redeem let _ = Self::check_redeem_period(_block_number); } } - /// Mock yield change impl Pallet { - fn record_user_staking_revenue(currency_id: CurrencyIdOf, revenue: BalanceOf) { - // vtoken total issued - let toal_issued = T::MultiCurrency::total_issuance(currency_id); - - // find out all holders having this vtoken. - for (who, token_symbol, _) in - UserStakingRevenue::::iter().filter(|(_, id, _)| *id == *currency_id) - { - let free_balance = T::MultiCurrency::free_balance(currency_id, &who); - let gain = free_balance.saturating_mul(revenue) / toal_issued; - UserStakingRevenue::::mutate(&who, token_symbol, |balance| { - *balance = balance.saturating_add(gain); - }) - } - } - fn update_redeem_record( currency_id: CurrencyIdOf, who: &T::AccountId, @@ -413,9 +313,9 @@ pub mod pallet { record.push((current_block, amount)); }) } else { - let mut new_recrod = Vec::with_capacity(1); - new_recrod.push((current_block, amount)); - RedeemRecord::::insert(who, currency_id, new_recrod); + let mut new_record = Vec::with_capacity(1); + new_record.push((current_block, amount)); + RedeemRecord::::insert(who, currency_id, new_record); } } @@ -423,8 +323,9 @@ pub mod pallet { for (who, currency_id, records) in RedeemRecord::::iter() { let redeem_period = StakingLockPeriod::::get(¤cy_id); let mut exist_redeem_record = Vec::new(); - for (_index, (when, amount)) in records.iter().cloned().enumerate() { - if n - when >= redeem_period { + for (when, amount) in records.iter().cloned() { + let rs = n.checked_sub(&when).ok_or(Error::::CalculationOverflow)?; + if rs >= redeem_period { T::MultiCurrency::deposit(currency_id, &who, amount)?; } else { exist_redeem_record.push((when, amount)); @@ -437,34 +338,18 @@ pub mod pallet { Ok(()) } - - fn mock_yield_change() -> u32 { - // Use block number as seed - let current_block = >::block_number(); - let random_result = T::RandomnessSource::random(¤t_block.encode()); - let random_sum = random_result.0 .0.iter().fold(0u32, |acc, x| acc + *x as u32); - - random_sum - } } #[pallet::genesis_config] pub struct GenesisConfig { pub pools: Vec<(CurrencyIdOf, BalanceOf)>, pub staking_lock_period: Vec<(CurrencyIdOf, T::BlockNumber)>, - pub rate_of_interest_each_block: Vec<(CurrencyIdOf, BalanceOf)>, - pub yield_rate: Vec<(CurrencyIdOf, Permill)>, } #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> GenesisConfig { - Self { - pools: vec![], - staking_lock_period: vec![], - rate_of_interest_each_block: vec![], - yield_rate: vec![], - } + Self { pools: vec![], staking_lock_period: vec![] } } } @@ -478,14 +363,6 @@ pub mod pallet { for (currency_id, period) in self.staking_lock_period.iter() { StakingLockPeriod::::insert(currency_id, period); } - - for (currency_id, reward_by_block) in self.rate_of_interest_each_block.iter() { - RateOfInterestEachBlock::::insert(currency_id, reward_by_block); - } - - for (currency_id, rate) in self.yield_rate.iter() { - YieldRate::::insert(currency_id, rate); - } } } } diff --git a/pallets/vtoken-mint/src/mock.rs b/pallets/vtoken-mint/src/mock.rs index 185955e7ad..ce2e662c83 100644 --- a/pallets/vtoken-mint/src/mock.rs +++ b/pallets/vtoken-mint/src/mock.rs @@ -24,14 +24,18 @@ use core::marker::PhantomData; use std::convert::TryInto; -use frame_support::{parameter_types, traits::GenesisBuild, PalletId}; +use frame_support::{ + parameter_types, + traits::{GenesisBuild, Hooks}, + PalletId, +}; use node_primitives::{CurrencyId, TokenSymbol}; use orml_traits::MultiCurrency; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentityLookup, UniqueSaturatedInto, Zero}, - AccountId32, DispatchError, DispatchResult, Permill, SaturatedConversion, + traits::{BlakeTwo256, IdentityLookup, UniqueSaturatedInto}, + AccountId32, DispatchError, DispatchResult, SaturatedConversion, }; use zenlink_protocol::{AssetBalance, AssetId, LocalAssetHandler, ZenlinkMultiAssets}; @@ -46,7 +50,6 @@ pub const KSM: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); pub const vKSM: CurrencyId = CurrencyId::VToken(TokenSymbol::KSM); pub const ALICE: AccountId = AccountId32::new([0u8; 32]); pub const BOB: AccountId = AccountId32::new([1u8; 32]); -pub const CENTS: Balance = 1_000_000_000_000 / 100; pub type BlockNumber = u64; pub type Amount = i128; @@ -66,8 +69,7 @@ frame_support::construct_runtime!( Assets: orml_tokens::{Pallet, Call, Storage, Event, Config}, PalletBalances: pallet_balances::{Pallet, Call, Storage, Config, Event}, VtokenMint: vtoken_mint::{Pallet, Call, Storage, Event}, - RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, - MinterReward: bifrost_minter_reward::{Pallet, Storage, Event}, + MinterReward: bifrost_minter_reward::{Pallet, Storage, Call,Event, Config}, ZenlinkProtocol: zenlink_protocol::{Pallet, Call, Storage, Event}, } ); @@ -151,45 +153,27 @@ impl orml_tokens::Config for Runtime { } parameter_types! { - pub const TwoYear: u32 = 1 * 365 * 2; - pub const RewardPeriod: u32 = 50; + pub const HalvingCycle: u32 = 1 * 365 * 2; + pub const RewardWindow: u32 = 50; pub const MaximumExtendedPeriod: u32 = 500; - pub const ShareWeightPalletId: PalletId = PalletId(*b"weight "); + pub const StableCurrencyId: CurrencyId = CurrencyId::Stable(TokenSymbol::AUSD); } impl bifrost_minter_reward::Config for Runtime { + type DexOperator = ZenlinkProtocol; type Event = Event; + type HalvingCycle = HalvingCycle; type MaximumExtendedPeriod = MaximumExtendedPeriod; - type MultiCurrency = Assets; - type RewardPeriod = RewardPeriod; + type MultiCurrency = Currencies; + type RewardWindow = RewardWindow; type ShareWeight = Balance; - type SystemPalletId = ShareWeightPalletId; - type TwoYear = TwoYear; - type ZenlinkOperator = ZenlinkProtocol; + type StableCurrencyId = StableCurrencyId; } -impl pallet_randomness_collective_flip::Config for Runtime {} - -parameter_types! { - // 3 hours(1800 blocks) as an era - pub const VtokenMintDuration: u32 = 3 * 60 * 1; - pub const StakingPalletId: PalletId = PalletId(*b"staking "); -} -orml_traits::parameter_type_with_key! { - pub RateOfInterestEachBlock: |currency_id: CurrencyId| -> Balance { - match currency_id { - &CurrencyId::Token(TokenSymbol::DOT) => 1 * CENTS, - &CurrencyId::Token(TokenSymbol::ETH) => 7 * CENTS, - _ => Zero::zero(), - } - }; -} impl crate::Config for Runtime { type Event = Event; type MinterReward = MinterReward; - type MultiCurrency = Assets; - type PalletId = StakingPalletId; - type RandomnessSource = RandomnessCollectiveFlip; + type MultiCurrency = Currencies; type WeightInfo = (); } @@ -207,7 +191,6 @@ impl zenlink_protocol::Config for Runtime { type GetExchangeFee = GetExchangeFee; type MultiAssetsHandler = MultiAssets; type PalletId = ZenlinkPalletId; - // type SelfParaId = SelfParaId; type SelfParaId = (); type TargetChains = (); type XcmExecutor = (); @@ -332,20 +315,24 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); + bifrost_minter_reward::GenesisConfig:: { + currency_weights: vec![ + (CurrencyId::Token(TokenSymbol::DOT), 1 * 1), + (CurrencyId::Token(TokenSymbol::ETH), 1 * 1), + (CurrencyId::Token(TokenSymbol::KSM), 1 * 3), + ], + reward_per_block: 300, + cycle_index: 1, + } + .assimilate_storage(&mut t) + .unwrap(); + crate::GenesisConfig:: { pools: vec![], staking_lock_period: vec![ (CurrencyId::Token(TokenSymbol::DOT), 28 * 1), (CurrencyId::Token(TokenSymbol::ETH), 14 * 1), ], - rate_of_interest_each_block: vec![ - (CurrencyId::Token(TokenSymbol::DOT), 019_025_875_190), // 100000.0 * 0.148/(365*24*600) - (CurrencyId::Token(TokenSymbol::ETH), 009_512_937_595), // 50000.0 * 0.082/(365*24*600) - ], - yield_rate: vec![ - (CurrencyId::Token(TokenSymbol::DOT), Permill::from_perthousand(148)), // 14.8% - (CurrencyId::Token(TokenSymbol::ETH), Permill::from_perthousand(82)), // 8.2% - ], } .assimilate_storage(&mut t) .unwrap(); @@ -353,3 +340,14 @@ impl ExtBuilder { t.into() } } + +// simulate block production +pub(crate) fn run_to_block(n: u64) { + while System::block_number() < n { + VtokenMint::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + VtokenMint::on_initialize(System::block_number()); + } +} diff --git a/pallets/vtoken-mint/src/tests.rs b/pallets/vtoken-mint/src/tests.rs index 403ff1a66c..94f565cd81 100644 --- a/pallets/vtoken-mint/src/tests.rs +++ b/pallets/vtoken-mint/src/tests.rs @@ -38,10 +38,12 @@ fn mint_vtoken_should_be_ok() { let to_sell_dot = 20; let minted_vdot = to_sell_dot * dot_price; - System::set_block_number(1); + run_to_block(1); // Alice sell 20 DOTs to mint vDOT. assert_ok!(VtokenMint::mint(Origin::signed(ALICE), vDOT, to_sell_dot)); + run_to_block(2); + println!("MInter: {:?}", MinterReward::current_round_start_at()); // Check event let mint_vtoken_event = @@ -95,9 +97,7 @@ fn redeem_token_should_be_ok() { let to_sell_vdot = 20; let minted_dot = to_sell_vdot * dot_pool / vdot_pool; - System::set_block_number(1); - System::on_initialize(1); - + run_to_block(1); // Alice sell 20 vDOTs to mint DOT. assert_ok!(VtokenMint::redeem(Origin::signed(ALICE), DOT, to_sell_vdot)); @@ -138,9 +138,14 @@ fn redeem_token_should_be_ok() { Error::::BalanceLow ); - System::set_block_number(40); - System::on_initialize(40); - System::on_finalize(40); + run_to_block(20); + + // Alice should have not received the minted dots, since dot redeem period is 28 blocks + // which is set in the mock + assert_eq!(Assets::free_balance(DOT, &ALICE), alice_dot); + + // After 30 blocks, Alice should received the minted dots + run_to_block(30); assert_eq!(Assets::free_balance(DOT, &ALICE), alice_dot + minted_dot); }); } diff --git a/pallets/vtoken-mint/src/weights.rs b/pallets/vtoken-mint/src/weights.rs new file mode 100644 index 0000000000..fd5ef16235 --- /dev/null +++ b/pallets/vtoken-mint/src/weights.rs @@ -0,0 +1,73 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2021 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for the pallet. +pub trait WeightInfo { + fn set_vtoken_pool() -> Weight; + fn set_token_staking_lock_period() -> Weight; + fn mint() -> Weight; + fn redeem() -> Weight; +} + +/// Weights for the pallet using the Bifrost node and recommended hardware. +pub struct BifrostWeight(PhantomData); +impl WeightInfo for BifrostWeight { + fn set_vtoken_pool() -> Weight { + (50_000_000 as Weight) + } + + fn set_token_staking_lock_period() -> Weight { + (50_000_000 as Weight) + } + + fn mint() -> Weight { + (50_000_000 as Weight) + } + + fn redeem() -> Weight { + (50_000_000 as Weight) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn set_vtoken_pool() -> Weight { + (50_000_000 as Weight) + } + + fn set_token_staking_lock_period() -> Weight { + (50_000_000 as Weight) + } + + fn mint() -> Weight { + (50_000_000 as Weight) + } + + fn redeem() -> Weight { + (50_000_000 as Weight) + } +} diff --git a/runtime/asgard/src/lib.rs b/runtime/asgard/src/lib.rs index 3052e36706..efa96bd62f 100644 --- a/runtime/asgard/src/lib.rs +++ b/runtime/asgard/src/lib.rs @@ -872,10 +872,8 @@ parameter_types! { impl bifrost_vtoken_mint::Config for Runtime { type Event = Event; type MinterReward = MinterReward; - type MultiCurrency = Tokens; - type PalletId = StakingPalletId; - type RandomnessSource = RandomnessCollectiveFlip; - type WeightInfo = weights::pallet_vtoken_mint::WeightInfo; + type MultiCurrency = Currencies; + type WeightInfo = bifrost_vtoken_mint::weights::BifrostWeight; } orml_traits::parameter_type_with_key! { @@ -904,31 +902,31 @@ parameter_types! { impl bifrost_charge_transaction_fee::Config for Runtime { type Balance = Balance; - type CurrenciesHandler = Currencies; type Currency = Balances; + type DexOperator = ZenlinkProtocol; type Event = Event; + type MultiCurrency = Currencies; type NativeCurrencyId = NativeCurrencyId; type OnUnbalanced = (); type WeightInfo = (); - type ZenlinkOperator = ZenlinkProtocol; } parameter_types! { - pub const TwoYear: BlockNumber = DAYS * 365 * 2; - pub const RewardPeriod: BlockNumber = 50; - pub const MaximumExtendedPeriod: BlockNumber = 100; - pub const ShareWeightPalletId: PalletId = PalletId(*b"weight "); + pub const HalvingCycle: u32 = 60; + pub const RewardWindow: u32 = 10; + pub const MaximumExtendedPeriod: u32 = 20; + pub const StableCurrencyId: CurrencyId = CurrencyId::Stable(TokenSymbol::AUSD); } impl bifrost_minter_reward::Config for Runtime { + type DexOperator = ZenlinkProtocol; type Event = Event; + type HalvingCycle = HalvingCycle; type MaximumExtendedPeriod = MaximumExtendedPeriod; type MultiCurrency = Tokens; - type RewardPeriod = RewardPeriod; + type RewardWindow = RewardWindow; type ShareWeight = Balance; - type SystemPalletId = ShareWeightPalletId; - type TwoYear = TwoYear; - type ZenlinkOperator = ZenlinkProtocol; + type StableCurrencyId = StableCurrencyId; } parameter_types! { @@ -966,13 +964,14 @@ impl bifrost_salp::Config for Runtime { } parameter_types! { - pub const InterventionPercentage: Balance = 75; + pub const InterventionPercentage: Percent = Percent::from_percent(75); } impl bifrost_bancor::Config for Runtime { type Event = Event; type InterventionPercentage = InterventionPercentage; type MultiCurrenciesHandler = Currencies; + type WeightInfo = bifrost_bancor::weights::BifrostWeight; } parameter_types! { diff --git a/runtime/asgard/src/weights/mod.rs b/runtime/asgard/src/weights/mod.rs index ca54404001..3cdec6fda5 100644 --- a/runtime/asgard/src/weights/mod.rs +++ b/runtime/asgard/src/weights/mod.rs @@ -22,4 +22,3 @@ pub mod pallet_salp; pub mod pallet_vesting; -pub mod pallet_vtoken_mint;