From 84cbc0ec0a4e61bfbc09e0d4fd02728e98725ea0 Mon Sep 17 00:00:00 2001 From: yooml Date: Tue, 7 May 2024 21:56:31 +0800 Subject: [PATCH] Ve minting markup (#1237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add markup * fix: πŸ› markup_calc * feat: 🎸 add refresh * refactor: πŸ’‘ transfer to setlock * refactor: πŸ’‘ add position * fix: πŸ› balance_of * fix: πŸ› add UserLocked * fix: πŸ› fmt * fix: πŸ› runtime config * feat: 🎸 add fast_withdraw * incentive-minting * clouds-convert pallet * runtime config and benchmarking * fix cargo clippy * fix clippy * fix test * feat: 🎸 add bench & test * fix * fix: πŸ› test * fix: πŸ› bench * fix: πŸ› check-all * fix: πŸ› test * fix: πŸ› bench * fix: πŸ› fmt * fix: πŸ› clippy * fix: πŸ› test * fix: πŸ› set_incentive * fix: πŸ› charge * fix: πŸ› VoteWeightMultiplier * fix: πŸ› for test * fix: πŸ› for test --------- Co-authored-by: herryho --- Cargo.lock | 1 + pallets/clouds-convert/src/mock.rs | 7 +- pallets/clouds-convert/src/tests.rs | 2 +- pallets/farming/src/benchmarking.rs | 50 +- pallets/farming/src/gauge.rs | 300 +-------- pallets/farming/src/lib.rs | 178 ++--- pallets/farming/src/mock.rs | 8 +- pallets/farming/src/rewards.rs | 6 + pallets/farming/src/tests.rs | 181 ++--- pallets/fee-share/src/mock.rs | 5 + pallets/leverage-staking/src/mock.rs | 6 +- pallets/salp/src/mock.rs | 5 + pallets/slp/src/mocks/mock.rs | 5 + pallets/slp/src/mocks/mock_kusama.rs | 5 + pallets/slpx/src/mock.rs | 5 + pallets/stable-pool/src/mock.rs | 6 +- pallets/system-maker/src/mock.rs | 5 + pallets/system-staking/src/mock.rs | 7 + pallets/system-staking/src/tests.rs | 4 +- pallets/ve-minting/Cargo.toml | 3 +- pallets/ve-minting/src/benchmarking.rs | 143 +++- pallets/ve-minting/src/incentive.rs | 148 +++-- pallets/ve-minting/src/lib.rs | 621 ++++++++++++++++-- pallets/ve-minting/src/mock.rs | 13 +- pallets/ve-minting/src/tests.rs | 292 ++++++-- pallets/ve-minting/src/traits.rs | 291 +++++--- pallets/ve-minting/src/weights.rs | 217 ++++++ pallets/vtoken-minting/Cargo.toml | 4 +- pallets/vtoken-minting/src/benchmarking.rs | 79 ++- pallets/vtoken-minting/src/lib.rs | 411 +++++++++++- pallets/vtoken-minting/src/mock.rs | 102 ++- pallets/vtoken-minting/src/tests.rs | 222 ++++++- pallets/vtoken-minting/src/weights.rs | 33 + runtime/bifrost-kusama/src/lib.rs | 7 + .../src/weights/bifrost_vtoken_minting.rs | 24 + runtime/bifrost-polkadot/src/lib.rs | 17 +- .../src/weights/bifrost_ve_minting.rs | 212 ++++++ .../src/weights/bifrost_vtoken_minting.rs | 24 + 38 files changed, 2820 insertions(+), 829 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe6746d80c..21ba5be2d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1898,6 +1898,7 @@ dependencies = [ "bifrost-primitives", "bifrost-runtime-common", "bifrost-slp", + "bifrost-ve-minting", "cumulus-primitives-core", "env_logger", "frame-benchmarking", diff --git a/pallets/clouds-convert/src/mock.rs b/pallets/clouds-convert/src/mock.rs index c6712c7b5e..b1062a5fa4 100644 --- a/pallets/clouds-convert/src/mock.rs +++ b/pallets/clouds-convert/src/mock.rs @@ -86,6 +86,7 @@ impl frame_system::Config for Runtime { type SystemWeightInfo = (); type Version = (); type MaxConsumers = frame_support::traits::ConstU32<16>; + type RuntimeTask = (); } parameter_types! { @@ -181,7 +182,9 @@ parameter_types! { pub const Week: BlockNumber = 50400; // a week pub const MaxBlock: BlockNumber = 10512000; // four years pub const Multiplier: Balance = 10_u128.pow(12); - pub const VoteWeightMultiplier: Balance = 3; + pub const VoteWeightMultiplier: Balance = 1; + pub const MaxPositions: u32 = 10; + pub const MarkupRefreshLimit: u32 = 100; } impl bifrost_ve_minting::Config for Runtime { @@ -197,6 +200,8 @@ impl bifrost_ve_minting::Config for Runtime { type MaxBlock = MaxBlock; type Multiplier = Multiplier; type VoteWeightMultiplier = VoteWeightMultiplier; + type MaxPositions = MaxPositions; + type MarkupRefreshLimit = MarkupRefreshLimit; } pub struct ExtBuilder { diff --git a/pallets/clouds-convert/src/tests.rs b/pallets/clouds-convert/src/tests.rs index c8794dbae2..8f2776bac9 100644 --- a/pallets/clouds-convert/src/tests.rs +++ b/pallets/clouds-convert/src/tests.rs @@ -71,7 +71,7 @@ fn clouds_to_vebnc_should_work() { // check the veBNC balance of Bob assert_eq!( ::VeMinting::balance_of(&BOB, None).unwrap(), - bob_old_vebnc_balance + 79817139200 + bob_old_vebnc_balance + 19939046400 ); // check the new pool balance diff --git a/pallets/farming/src/benchmarking.rs b/pallets/farming/src/benchmarking.rs index d82e9b1043..fe5e4504ac 100644 --- a/pallets/farming/src/benchmarking.rs +++ b/pallets/farming/src/benchmarking.rs @@ -29,6 +29,7 @@ use crate::{Pallet as Farming, *}; benchmarks! { on_initialize {}:{Farming::::on_initialize(BlockNumberFor::::from(10u32));} create_farming_pool { + let caller: T::AccountId = whitelisted_caller(); let token_amount = BalanceOf::::unique_saturated_from(1000u128); let default_currency_id = CurrencyIdOf::::default(); let tokens_proportion = vec![(default_currency_id, Perbill::from_percent(100))]; @@ -37,7 +38,7 @@ benchmarks! { }: _(RawOrigin::Root, tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -55,7 +56,7 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -63,7 +64,7 @@ benchmarks! { 5, )); let charge_rewards = vec![(default_currency_id,BalanceOf::::unique_saturated_from(300000u128))]; - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); }: _(RawOrigin::Signed(caller.clone()), 0, token_amount, None) withdraw { @@ -77,7 +78,7 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -85,7 +86,7 @@ benchmarks! { 5, )); let charge_rewards = vec![(default_currency_id,BalanceOf::::unique_saturated_from(300000u128))]; - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); assert_ok!(Farming::::deposit(RawOrigin::Signed(caller.clone()).into(), 0, token_amount, None)); }: _(RawOrigin::Signed(caller.clone()), 0, None) @@ -100,7 +101,7 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -108,7 +109,7 @@ benchmarks! { 5, )); let charge_rewards = vec![(default_currency_id,BalanceOf::::unique_saturated_from(300000u128))]; - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); assert_ok!(Farming::::deposit(RawOrigin::Signed(caller.clone()).into(), 0, token_amount, None)); System::::set_block_number(System::::block_number() + BlockNumberFor::::from(10u32)); Farming::::on_initialize(BlockNumberFor::::from(0u32)); @@ -125,7 +126,7 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -133,7 +134,7 @@ benchmarks! { 5, )); let charge_rewards = vec![(default_currency_id,BalanceOf::::unique_saturated_from(300000u128))]; - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); assert_ok!(Farming::::deposit(RawOrigin::Signed(caller.clone()).into(), 0, token_amount, Some((BalanceOf::::unique_saturated_from(100u128), BlockNumberFor::::from(100u32))))); // System::::set_block_number(System::::block_number() + BlockNumberFor::::from(10u32)); }: _(RawOrigin::Signed(caller.clone()), 0) @@ -149,7 +150,7 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -157,7 +158,7 @@ benchmarks! { 5, )); let charge_rewards = vec![(default_currency_id,BalanceOf::::unique_saturated_from(300000u128))]; - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); assert_ok!(Farming::::deposit(RawOrigin::Signed(caller.clone()).into(), 0, token_amount, None)); }: _(RawOrigin::Signed(caller.clone()), 0) @@ -174,14 +175,14 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards.clone())), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards.clone())), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), BlockNumberFor::::from(6u32), 5, )); - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); System::::set_block_number(System::::block_number() + BlockNumberFor::::from(10u32)); Farming::::on_initialize(BlockNumberFor::::from(0u32)); assert_ok!(Farming::::close_pool(RawOrigin::Root.into(), pid)); @@ -195,7 +196,7 @@ benchmarks! { Some(BlockNumberFor::::from(7u32)), Some(BlockNumberFor::::from(6u32)), Some(5), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)) + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)) ) force_retire_pool { @@ -211,14 +212,14 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards.clone())), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards.clone())), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), BlockNumberFor::::from(6u32), 5, )); - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); System::::set_block_number(System::::block_number() + BlockNumberFor::::from(10u32)); Farming::::on_initialize(BlockNumberFor::::from(0u32)); assert_ok!(Farming::::close_pool(RawOrigin::Root.into(), pid)); @@ -238,7 +239,7 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards.clone())), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards.clone())), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -248,6 +249,7 @@ benchmarks! { }: _(RawOrigin::Root,pid) edit_pool { + let caller: T::AccountId = whitelisted_caller(); let token_amount = BalanceOf::::unique_saturated_from(1000u128); let default_currency_id = CurrencyIdOf::::default(); let tokens_proportion = vec![(default_currency_id, Perbill::from_percent(100))]; @@ -256,7 +258,7 @@ benchmarks! { assert_ok!(Farming::::create_farming_pool(RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards.clone())), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards.clone())), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -281,13 +283,13 @@ benchmarks! { assert_ok!(Farming::::create_farming_pool(RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), BlockNumberFor::::from(6u32), 5)); - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); System::::set_block_number(System::::block_number() + BlockNumberFor::::from(10u32)); Farming::::on_initialize(BlockNumberFor::::from(0u32)); }: _(RawOrigin::Root, 0) @@ -303,7 +305,7 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -311,7 +313,7 @@ benchmarks! { 5, )); let charge_rewards = vec![(default_currency_id,BalanceOf::::unique_saturated_from(300000u128))]; - }: _(RawOrigin::Signed(caller.clone()), 0, charge_rewards) + }: _(RawOrigin::Signed(caller.clone()), 0, charge_rewards, false) force_gauge_claim { let caller: T::AccountId = whitelisted_caller(); @@ -324,7 +326,7 @@ benchmarks! { RawOrigin::Root.into(), tokens_proportion.clone(), basic_rewards.clone(), - Some((default_currency_id, BlockNumberFor::::from(1000u32), gauge_basic_rewards)), + Some((BlockNumberFor::::from(1000u32), gauge_basic_rewards)), BalanceOf::::unique_saturated_from(0u128), BlockNumberFor::::from(0u32), BlockNumberFor::::from(7u32), @@ -332,7 +334,7 @@ benchmarks! { 5, )); let charge_rewards = vec![(default_currency_id,BalanceOf::::unique_saturated_from(300000u128))]; - assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards)); + assert_ok!(Farming::::charge(RawOrigin::Signed(caller.clone()).into(), 0, charge_rewards, false)); assert_ok!(Farming::::deposit(RawOrigin::Signed(caller.clone()).into(), 0, token_amount, Some((BalanceOf::::unique_saturated_from(100u128), BlockNumberFor::::from(100u32))))); assert_ok!(Farming::::set_retire_limit(RawOrigin::Root.into(), 10)); }: _(RawOrigin::Root, 0) diff --git a/pallets/farming/src/gauge.rs b/pallets/farming/src/gauge.rs index 74a95414ac..f5c7d33114 100644 --- a/pallets/farming/src/gauge.rs +++ b/pallets/farming/src/gauge.rs @@ -41,26 +41,6 @@ pub struct GaugeInfo { pub last_claim_block: BlockNumberFor, } -impl GaugeInfo -where - BalanceOf: Default + HasCompact, - BlockNumberFor: Default, -{ - fn new(who: AccountIdOf) -> Self { - Self { - who, - gauge_amount: Default::default(), - total_time_factor: Default::default(), - latest_time_factor: Default::default(), - claimed_time_factor: Default::default(), - gauge_start_block: Default::default(), - gauge_stop_block: Default::default(), - gauge_last_block: Default::default(), - last_claim_block: Default::default(), - } - } -} - /// The Reward Pool Info. #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct GaugePoolInfo { @@ -92,7 +72,6 @@ where { pub fn new( pid: PoolId, - token: CurrencyIdOf, keeper: AccountIdOf, reward_issuer: AccountIdOf, gauge_basic_rewards: BTreeMap, @@ -101,7 +80,7 @@ where ) -> Self { Self { pid, - token, + token: Default::default(), keeper, reward_issuer, rewards: BTreeMap::new(), @@ -123,16 +102,13 @@ where pub fn create_gauge_pool( pid: PoolId, pool_info: &mut PoolInfo, CurrencyIdOf, AccountIdOf, BlockNumberFor>, - gauge_token: CurrencyIdOf, gauge_basic_rewards: BTreeMap, BalanceOf>, max_block: BlockNumberFor, ) -> DispatchResult { - let gid = Self::gauge_pool_next_id(); - pool_info.gauge = Some(gid); + pool_info.gauge = Some(pid); let current_block_number = frame_system::Pallet::::block_number(); let gauge_pool_info = GaugePoolInfo::new( pid, - gauge_token, pool_info.keeper.clone(), pool_info.reward_issuer.clone(), gauge_basic_rewards, @@ -140,265 +116,14 @@ where current_block_number, ); - GaugePoolInfos::::insert(gid, &gauge_pool_info); + GaugePoolInfos::::insert(pid, &gauge_pool_info); GaugePoolNextId::::mutate(|id| -> DispatchResult { *id = id.checked_add(1).ok_or(ArithmeticError::Overflow)?; Ok(()) })?; - Ok(()) - } - - pub fn gauge_add( - who: &AccountIdOf, - gid: PoolId, - gauge_value: BalanceOf, - gauge_block: BlockNumberFor, - ) -> DispatchResult { - GaugePoolInfos::::mutate(gid, |gauge_pool_info_old| -> DispatchResult { - if let Some(mut gauge_pool_info) = gauge_pool_info_old.take() { - let current_block_number = frame_system::Pallet::::block_number(); - gauge_pool_info.gauge_last_block = current_block_number; - gauge_pool_info.gauge_amount = gauge_pool_info - .gauge_amount - .checked_add(&gauge_value) - .ok_or(ArithmeticError::Overflow)?; - let mut gauge_info = - GaugeInfos::::get(gid, who).unwrap_or_else(|| GaugeInfo::new(who.clone())); - - ensure!( - gauge_info.gauge_stop_block >= current_block_number || - gauge_info.gauge_stop_block == Default::default(), - Error::::LastGaugeNotClaim - ); - - ensure!( - gauge_pool_info.max_block >= - gauge_info - .gauge_stop_block - .checked_sub(&gauge_info.gauge_start_block) - .ok_or(ArithmeticError::Overflow)? - .checked_add(&gauge_block) - .ok_or(ArithmeticError::Overflow)?, - Error::::GaugeMaxBlockOverflow - ); - let incease_total_time_factor = if gauge_info.gauge_amount.is_zero() { - gauge_info.gauge_stop_block = current_block_number; - gauge_info.gauge_start_block = current_block_number; - gauge_info.last_claim_block = current_block_number; - gauge_info.total_time_factor = gauge_block - .saturated_into::() - .checked_mul(gauge_value.saturated_into::()) - .ok_or(ArithmeticError::Overflow)?; - gauge_info.total_time_factor - } else { - let time_factor_a = gauge_value - .saturated_into::() - .checked_mul( - (gauge_info - .gauge_stop_block - .checked_sub(¤t_block_number) - .ok_or(ArithmeticError::Overflow)?) - .saturated_into::(), - ) - .ok_or(ArithmeticError::Overflow)?; - let time_factor_b = gauge_block - .saturated_into::() - .checked_mul( - (gauge_value - .checked_add(&gauge_info.gauge_amount) - .ok_or(ArithmeticError::Overflow)?) - .saturated_into::(), - ) - .ok_or(ArithmeticError::Overflow)?; - let incease_total_time_factor = time_factor_a - .checked_add(time_factor_b) - .ok_or(ArithmeticError::Overflow)?; - gauge_info.total_time_factor = gauge_info - .total_time_factor - .checked_add(incease_total_time_factor) - .ok_or(ArithmeticError::Overflow)?; - // latest_time_factor only increases in not first gauge_deposit - let increase_latest_time_factor = gauge_info - .gauge_amount - .saturated_into::() - .checked_mul( - (current_block_number - .checked_sub(&gauge_info.gauge_last_block) - .ok_or(ArithmeticError::Overflow)?) - .saturated_into::(), - ) - .ok_or(ArithmeticError::Overflow)?; - gauge_info.latest_time_factor = gauge_info - .latest_time_factor - .checked_add(increase_latest_time_factor) - .ok_or(ArithmeticError::Overflow)?; - incease_total_time_factor - }; - - gauge_info.gauge_last_block = current_block_number; - gauge_info.gauge_amount = gauge_info - .gauge_amount - .checked_add(&gauge_value) - .ok_or(ArithmeticError::Overflow)?; - gauge_info.gauge_stop_block = gauge_info - .gauge_stop_block - .checked_add(&gauge_block) - .ok_or(ArithmeticError::Overflow)?; - - gauge_pool_info.total_time_factor = gauge_pool_info - .total_time_factor - .checked_add(incease_total_time_factor) - .ok_or(ArithmeticError::Overflow)?; - T::MultiCurrency::transfer( - gauge_pool_info.token, - who, - &gauge_pool_info.keeper, - gauge_value, - )?; - GaugeInfos::::insert(gid, who, gauge_info); - *gauge_pool_info_old = Some(gauge_pool_info); - Ok(()) - } else { - Err(Error::::GaugePoolNotExist)? - } - })?; - Ok(()) - } - - pub fn gauge_claim_inner(who: &AccountIdOf, gid: PoolId) -> DispatchResult { - if !GaugeInfos::::contains_key(gid, who) { - return Ok(()); - } - let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); - let mut gauge_pool_info = - GaugePoolInfos::::get(gid).ok_or(Error::::GaugePoolNotExist)?; - let pool_info = - PoolInfos::::get(gauge_pool_info.pid).ok_or(Error::::PoolDoesNotExist)?; - GaugeInfos::::mutate_exists(gid, who, |maybe_gauge_info| -> DispatchResult { - if let Some(mut gauge_info) = maybe_gauge_info.take() { - ensure!( - gauge_info.gauge_start_block <= current_block_number, - Error::::CanNotClaim - ); - let start_block = if current_block_number > gauge_info.gauge_stop_block { - gauge_info.gauge_stop_block - } else { - current_block_number - }; - - let latest_claimed_time_factor = gauge_info.latest_time_factor + - gauge_info - .gauge_amount - .saturated_into::() - .checked_mul( - (start_block - .checked_sub(&gauge_info.gauge_last_block) - .ok_or(ArithmeticError::Overflow)?) - .saturated_into::(), - ) - .ok_or(ArithmeticError::Overflow)?; - let gauge_rate = Perbill::from_rational( - latest_claimed_time_factor - .checked_sub(gauge_info.claimed_time_factor) - .ok_or(ArithmeticError::Overflow)?, - gauge_pool_info.total_time_factor, - ); - let total_shares = - U256::from(pool_info.total_shares.to_owned().saturated_into::()); - let share_info = SharesAndWithdrawnRewards::::get(gauge_pool_info.pid, who) - .ok_or(Error::::ShareInfoNotExists)?; - gauge_pool_info.rewards.iter_mut().try_for_each( - |( - reward_currency, - (reward_amount, total_gauged_reward, total_withdrawn_reward), - )| - -> DispatchResult { - let reward = reward_amount - .checked_sub(&total_gauged_reward) - .ok_or(ArithmeticError::Overflow)?; - // gauge_reward = gauge rate * gauge rewards * existing rewards in the - // gauge pool - let gauge_reward = gauge_rate * reward; - // reward_to_claim = farming rate * gauge rate * gauge rewards * - // existing rewards in the gauge pool - let reward_to_claim: BalanceOf = u128::try_from( - U256::from(share_info.share.to_owned().saturated_into::()) - .saturating_mul(U256::from( - gauge_reward.to_owned().saturated_into::(), - )) - .checked_div(total_shares) - .unwrap_or_default(), - ) - .map_err(|_| ArithmeticError::Overflow)? - .unique_saturated_into(); - *total_gauged_reward = total_gauged_reward - .checked_add(&gauge_reward) - .ok_or(ArithmeticError::Overflow)?; - *total_withdrawn_reward = total_withdrawn_reward - .checked_add(&reward_to_claim) - .ok_or(ArithmeticError::Overflow)?; - - let ed = T::MultiCurrency::minimum_balance(*reward_currency); - let mut account_to_send = who.clone(); - - if reward_to_claim < ed { - let receiver_balance = - T::MultiCurrency::total_balance(*reward_currency, &who); - - let receiver_balance_after = receiver_balance - .checked_add(&reward_to_claim) - .ok_or(ArithmeticError::Overflow)?; - if receiver_balance_after < ed { - account_to_send = T::TreasuryAccount::get(); - } - } - T::MultiCurrency::transfer( - *reward_currency, - &gauge_pool_info.reward_issuer, - &account_to_send, - reward_to_claim, - ) - }, - )?; - gauge_info.last_claim_block = current_block_number; - gauge_info.claimed_time_factor = latest_claimed_time_factor; - if gauge_info.gauge_stop_block <= current_block_number { - let ed = T::MultiCurrency::minimum_balance(gauge_pool_info.token); - let mut account_to_send = who.clone(); - - if gauge_info.gauge_amount < ed { - let receiver_balance = - T::MultiCurrency::total_balance(gauge_pool_info.token, &who); - - let receiver_balance_after = receiver_balance - .checked_add(&gauge_info.gauge_amount) - .ok_or(ArithmeticError::Overflow)?; - if receiver_balance_after < ed { - account_to_send = T::TreasuryAccount::get(); - } - } - T::MultiCurrency::transfer( - gauge_pool_info.token, - &gauge_pool_info.keeper, - &account_to_send, - gauge_info.gauge_amount, - )?; - gauge_pool_info.total_time_factor = gauge_pool_info - .total_time_factor - .checked_sub(gauge_info.total_time_factor) - .ok_or(ArithmeticError::Overflow)?; - gauge_pool_info.gauge_amount = gauge_pool_info - .gauge_amount - .checked_sub(&gauge_info.gauge_amount) - .ok_or(ArithmeticError::Overflow)?; - } else { - *maybe_gauge_info = Some(gauge_info); - }; - GaugePoolInfos::::insert(gid, gauge_pool_info); - } - Ok(()) - })?; + let controller = T::GaugeRewardIssuer::get().into_sub_account_truncating(pid); + T::VeMinting::set_incentive(pid, Some(max_block), Some(controller)); Ok(()) } @@ -518,4 +243,19 @@ where }; Ok(result_vec) } + + pub fn update_reward(who: &AccountIdOf, pid: PoolId) -> Result<(), DispatchError> { + let pool_info = PoolInfos::::get(pid).ok_or(Error::::PoolDoesNotExist)?; + let share_info = + SharesAndWithdrawnRewards::::get(pid, who).ok_or(Error::::ShareInfoNotExists)?; + if T::VeMinting::balance_of(who, None)? == BalanceOf::::zero() { + return Ok(()); + } + T::VeMinting::update_reward( + pid, + Some(who), + Some((share_info.share, pool_info.total_shares)), + )?; + Ok(()) + } } diff --git a/pallets/farming/src/lib.rs b/pallets/farming/src/lib.rs index 3628feb7a0..3f4d1a7060 100644 --- a/pallets/farming/src/lib.rs +++ b/pallets/farming/src/lib.rs @@ -62,6 +62,7 @@ pub type CurrencyIdOf = <::MultiCurrency as MultiCurrency< type BalanceOf = <::MultiCurrency as MultiCurrency>>::Balance; +use bifrost_ve_minting::VeMintingInterface; use parity_scale_codec::FullCodec; use sp_std::fmt::Debug; @@ -119,6 +120,9 @@ pub mod pallet { #[pallet::constant] type WhitelistMaximumLimit: Get; + + #[pallet::constant] + type GaugeRewardIssuer: Get; } #[pallet::event] @@ -143,6 +147,7 @@ pub mod pallet { who: AccountIdOf, pid: PoolId, rewards: Vec<(CurrencyIdOf, BalanceOf)>, + if_gauge: bool, }, Deposited { who: AccountIdOf, @@ -354,25 +359,16 @@ pub mod pallet { _ => (), }); - GaugePoolInfos::::iter().for_each( - |(gid, mut gauge_pool_info)| match gauge_pool_info.gauge_state { + GaugePoolInfos::::iter().for_each(|(gid, gauge_pool_info)| { + match gauge_pool_info.gauge_state { GaugeState::Bonded => { - gauge_pool_info.gauge_basic_rewards.clone().iter().for_each( - |(reward_currency_id, reward_amount)| { - gauge_pool_info - .rewards - .entry(*reward_currency_id) - .and_modify(|(total_reward, _, _)| { - *total_reward = total_reward.saturating_add(*reward_amount); - }) - .or_insert((*reward_amount, Zero::zero(), Zero::zero())); - }, - ); - GaugePoolInfos::::insert(gid, &gauge_pool_info); + let rewards = gauge_pool_info.gauge_basic_rewards.into_iter().collect(); + T::VeMinting::auto_notify_reward(gid, n, rewards).unwrap_or_default(); }, _ => (), - }, - ); + } + }); + if n == Self::boost_pool_infos().end_round { Self::end_boost_round_inner(); Self::auto_start_boost_round(); @@ -394,11 +390,7 @@ pub mod pallet { origin: OriginFor, tokens_proportion: Vec<(CurrencyIdOf, Perbill)>, basic_rewards: Vec<(CurrencyIdOf, BalanceOf)>, - gauge_init: Option<( - CurrencyIdOf, - BlockNumberFor, - Vec<(CurrencyIdOf, BalanceOf)>, - )>, + gauge_init: Option<(BlockNumberFor, Vec<(CurrencyIdOf, BalanceOf)>)>, min_deposit_to_start: BalanceOf, #[pallet::compact] after_block_to_start: BlockNumberFor, #[pallet::compact] withdraw_limit_time: BlockNumberFor, @@ -430,17 +422,11 @@ pub mod pallet { withdraw_limit_count, ); - if let Some((gauge_token, max_block, gauge_basic_rewards)) = gauge_init { + if let Some((max_block, gauge_basic_rewards)) = gauge_init { let gauge_basic_rewards_map: BTreeMap, BalanceOf> = gauge_basic_rewards.into_iter().map(|(k, v)| (k, v)).collect(); - Self::create_gauge_pool( - pid, - &mut pool_info, - gauge_token, - gauge_basic_rewards_map, - max_block, - )?; + Self::create_gauge_pool(pid, &mut pool_info, gauge_basic_rewards_map, max_block)?; }; PoolInfos::::insert(pid, &pool_info); @@ -459,28 +445,47 @@ pub mod pallet { origin: OriginFor, pid: PoolId, rewards: Vec<(CurrencyIdOf, BalanceOf)>, + if_gauge: bool, ) -> DispatchResult { let exchanger = ensure_signed(origin)?; let mut pool_info = Self::pool_infos(&pid).ok_or(Error::::PoolDoesNotExist)?; - ensure!( - pool_info.state == PoolState::UnCharged || pool_info.state == PoolState::Ongoing, - Error::::InvalidPoolState - ); - rewards.iter().try_for_each(|(reward_currency, reward)| -> DispatchResult { - T::MultiCurrency::transfer( - *reward_currency, - &exchanger, - &pool_info.reward_issuer, - *reward, - ) - })?; - if pool_info.state == PoolState::UnCharged { - pool_info.state = PoolState::Charged - } - PoolInfos::::insert(&pid, pool_info); - Self::deposit_event(Event::Charged { who: exchanger, pid, rewards }); + match if_gauge { + true => { + let gauge_reward_issuer = + T::GaugeRewardIssuer::get().into_sub_account_truncating(pid); + rewards.iter().try_for_each(|(reward_currency, reward)| -> DispatchResult { + T::MultiCurrency::transfer( + *reward_currency, + &exchanger, + &gauge_reward_issuer, + *reward, + ) + })?; + }, + false => { + ensure!( + pool_info.state == PoolState::UnCharged || + pool_info.state == PoolState::Ongoing, + Error::::InvalidPoolState + ); + rewards.iter().try_for_each(|(reward_currency, reward)| -> DispatchResult { + T::MultiCurrency::transfer( + *reward_currency, + &exchanger, + &pool_info.reward_issuer, + *reward, + ) + })?; + if pool_info.state == PoolState::UnCharged { + pool_info.state = PoolState::Charged + } + PoolInfos::::insert(&pid, pool_info); + }, + }; + + Self::deposit_event(Event::Charged { who: exchanger, pid, rewards, if_gauge }); Ok(()) } @@ -522,18 +527,7 @@ pub mod pallet { }, )?; Self::add_share(&exchanger, pid, &mut pool_info, add_value); - - match gauge_info { - Some((gauge_value, gauge_block)) => { - Self::gauge_add( - &exchanger, - pool_info.gauge.ok_or(Error::::GaugePoolNotExist)?, - gauge_value, - gauge_block, - )?; - }, - None => (), - }; + Self::update_reward(&exchanger, pid)?; Self::deposit_event(Event::Deposited { who: exchanger, pid, add_value, gauge_info }); Ok(()) @@ -564,6 +558,7 @@ pub mod pallet { ); Self::remove_share(&exchanger, pid, remove_value, pool_info.withdraw_limit_time)?; + Self::update_reward(&exchanger, pid)?; Self::deposit_event(Event::Withdrawn { who: exchanger, pid, remove_value }); Ok(()) @@ -591,9 +586,6 @@ pub mod pallet { ); Self::claim_rewards(&exchanger, pid)?; - if let Some(ref gid) = pool_info.gauge { - Self::gauge_claim_inner(&exchanger, *gid)?; - } Self::process_withdraw_list(&exchanger, pid, &pool_info, true)?; Self::deposit_event(Event::Claimed { who: exchanger, pid }); @@ -631,10 +623,6 @@ pub mod pallet { } let who = share_info.who; Self::remove_share(&who, pid, None, withdraw_limit_time)?; - Self::claim_rewards(&who, pid)?; - if let Some(ref gid) = pool_info.gauge { - Self::gauge_claim_inner(&who, *gid)?; - } Self::process_withdraw_list(&who, pid, &pool_info, true)?; } @@ -693,11 +681,7 @@ pub mod pallet { withdraw_limit_time: Option>, claim_limit_time: Option>, withdraw_limit_count: Option, - gauge_init: Option<( - CurrencyIdOf, - BlockNumberFor, - Vec<(CurrencyIdOf, BalanceOf)>, - )>, + gauge_init: Option<(BlockNumberFor, Vec<(CurrencyIdOf, BalanceOf)>)>, ) -> DispatchResult { T::ControlOrigin::ensure_origin(origin)?; @@ -723,17 +707,11 @@ pub mod pallet { if let Some(withdraw_limit_count) = withdraw_limit_count { pool_info.withdraw_limit_count = withdraw_limit_count; }; - if let Some((gauge_token, max_block, gauge_basic_rewards)) = gauge_init { + if let Some((max_block, gauge_basic_rewards)) = gauge_init { let gauge_basic_rewards_map: BTreeMap, BalanceOf> = gauge_basic_rewards.into_iter().map(|(k, v)| (k, v)).collect(); - Self::create_gauge_pool( - pid, - &mut pool_info, - gauge_token, - gauge_basic_rewards_map, - max_block, - )?; + Self::create_gauge_pool(pid, &mut pool_info, gauge_basic_rewards_map, max_block)?; }; pool_info.total_shares = Default::default(); pool_info.rewards = BTreeMap::new(); @@ -823,37 +801,10 @@ pub mod pallet { // Check origin let who = ensure_signed(origin)?; - let mut gauge_pool_info = - GaugePoolInfos::::get(gid).ok_or(Error::::GaugePoolNotExist)?; - match gauge_pool_info.gauge_state { - GaugeState::Bonded => { - Self::gauge_claim_inner(&who, gid)?; - }, - GaugeState::Unbond => { - let current_block_number: BlockNumberFor = - frame_system::Pallet::::block_number(); - GaugeInfos::::mutate(gid, &who, |maybe_gauge_info| -> DispatchResult { - if let Some(gauge_info) = maybe_gauge_info.take() { - if gauge_info.gauge_stop_block <= current_block_number { - T::MultiCurrency::transfer( - gauge_pool_info.token, - &gauge_pool_info.keeper, - &who, - gauge_info.gauge_amount, - )?; - gauge_pool_info.total_time_factor = gauge_pool_info - .total_time_factor - .checked_sub(gauge_info.total_time_factor) - .ok_or(ArithmeticError::Overflow)?; - GaugePoolInfos::::insert(gid, gauge_pool_info); - } else { - *maybe_gauge_info = Some(gauge_info); - }; - } - Ok(()) - })?; - }, - } + let pool_info = PoolInfos::::get(gid).ok_or(Error::::PoolDoesNotExist)?; + let share_info = SharesAndWithdrawnRewards::::get(gid, &who) + .ok_or(Error::::ShareInfoNotExists)?; + T::VeMinting::get_rewards(gid, &who, Some((share_info.share, pool_info.total_shares)))?; Self::deposit_event(Event::GaugeWithdrawn { who, gid }); Ok(()) @@ -873,7 +824,14 @@ pub mod pallet { all_retired = false; break; } - Self::gauge_claim_inner(&gauge_info.who, gid)?; + let pool_info = PoolInfos::::get(gid).ok_or(Error::::PoolDoesNotExist)?; + let share_info = SharesAndWithdrawnRewards::::get(gid, &gauge_info.who) + .ok_or(Error::::ShareInfoNotExists)?; + T::VeMinting::get_rewards( + gid, + &gauge_info.who, + Some((share_info.share, pool_info.total_shares)), + )?; } if all_retired { diff --git a/pallets/farming/src/mock.rs b/pallets/farming/src/mock.rs index fc421b80a0..1eb4047541 100644 --- a/pallets/farming/src/mock.rs +++ b/pallets/farming/src/mock.rs @@ -128,6 +128,7 @@ parameter_types! { pub const FarmingBoostPalletId: PalletId = PalletId(*b"bf/fmbst"); pub const TreasuryAccount: AccountId32 = TREASURY_ACCOUNT; pub const WhitelistMaximumLimit: u32 = 10; + pub const FarmingGaugeRewardIssuerPalletId: PalletId = PalletId(*b"bf/fmgar"); } ord_parameter_types! { @@ -147,6 +148,7 @@ impl bifrost_farming::Config for Runtime { type VeMinting = VeMinting; type BlockNumberToBalance = ConvertInto; type WhitelistMaximumLimit = WhitelistMaximumLimit; + type GaugeRewardIssuer = FarmingGaugeRewardIssuerPalletId; } parameter_types! { @@ -156,7 +158,9 @@ parameter_types! { pub const Week: BlockNumber = 50400; // a week pub const MaxBlock: BlockNumber = 10512000; // four years pub const Multiplier: Balance = 10_u128.pow(12); - pub const VoteWeightMultiplier: Balance = 3; + pub const VoteWeightMultiplier: Balance = 1; + pub const MaxPositions: u32 = 10; + pub const MarkupRefreshLimit: u32 = 100; } impl bifrost_ve_minting::Config for Runtime { @@ -172,6 +176,8 @@ impl bifrost_ve_minting::Config for Runtime { type MaxBlock = MaxBlock; type Multiplier = Multiplier; type VoteWeightMultiplier = VoteWeightMultiplier; + type MaxPositions = MaxPositions; + type MarkupRefreshLimit = MarkupRefreshLimit; } ord_parameter_types! { diff --git a/pallets/farming/src/rewards.rs b/pallets/farming/src/rewards.rs index 5454041705..64f321df8f 100644 --- a/pallets/farming/src/rewards.rs +++ b/pallets/farming/src/rewards.rs @@ -291,6 +291,12 @@ impl Pallet { } pub fn claim_rewards(who: &T::AccountId, pool: PoolId) -> DispatchResult { + if let Some(_) = GaugePoolInfos::::get(pool) { + let pool_info = PoolInfos::::get(pool).ok_or(Error::::PoolDoesNotExist)?; + let share_info = SharesAndWithdrawnRewards::::get(pool, who) + .ok_or(Error::::ShareInfoNotExists)?; + T::VeMinting::get_rewards(pool, who, Some((share_info.share, pool_info.total_shares)))?; + } SharesAndWithdrawnRewards::::mutate_exists( pool, who, diff --git a/pallets/farming/src/tests.rs b/pallets/farming/src/tests.rs index adc142cdf4..e86f2cc47d 100644 --- a/pallets/farming/src/tests.rs +++ b/pallets/farming/src/tests.rs @@ -21,85 +21,8 @@ #![cfg(test)] use crate::{mock::*, *}; -use bifrost_asset_registry::AssetMetadata; -use bifrost_primitives::TokenInfo; -use bifrost_runtime_common::milli; use bifrost_ve_minting::VeMintingInterface; -use frame_support::{assert_err, assert_noop, assert_ok}; - -fn asset_registry() { - let items = vec![(KSM, 10 * milli::(KSM)), (BNC, 10 * milli::(BNC))]; - for (currency_id, metadata) in items.iter().map(|(currency_id, minimal_balance)| { - ( - currency_id, - AssetMetadata { - name: currency_id.name().map(|s| s.as_bytes().to_vec()).unwrap_or_default(), - symbol: currency_id.symbol().map(|s| s.as_bytes().to_vec()).unwrap_or_default(), - decimals: currency_id.decimals().unwrap_or_default(), - minimal_balance: *minimal_balance, - }, - ) - }) { - AssetRegistry::do_register_metadata(*currency_id, &metadata).expect("Token register"); - } -} - -#[test] -fn boost() { - ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { - asset_registry(); - System::set_block_number(System::block_number() + 20); - assert_ok!(VeMinting::set_config( - RuntimeOrigin::signed(ALICE), - Some(0), - Some(7 * 86400 / 12) - )); - - System::set_block_number(System::block_number() + 40); - assert_ok!(VeMinting::create_lock_inner( - &CHARLIE, - 20_000_000_000, - System::block_number() + 4 * 365 * 86400 / 12 - )); - assert_ok!(VeMinting::increase_amount(RuntimeOrigin::signed(CHARLIE), 80_000_000_000)); - assert_eq!(VeMinting::balance_of(&CHARLIE, None), Ok(399146883040)); - - let (pid, tokens) = init_no_gauge(); - assert_ok!(Farming::deposit(RuntimeOrigin::signed(CHARLIE), pid, tokens, None)); - assert_ok!(Farming::set_retire_limit(RuntimeOrigin::signed(ALICE), 10)); - assert_err!( - Farming::claim(RuntimeOrigin::signed(ALICE), pid), - Error::::InvalidPoolState - ); - System::set_block_number(System::block_number() + 100); - Farming::on_initialize(0); - assert_ok!(Farming::claim(RuntimeOrigin::signed(CHARLIE), pid)); - assert_eq!(Tokens::free_balance(KSM, &CHARLIE), 999999999000); - - let vote_list: Vec<(u32, Percent)> = vec![(pid, Percent::from_percent(100))]; - let charge_list = vec![(KSM, 1000)]; - assert_ok!(Farming::add_boost_pool_whitelist(RuntimeOrigin::signed(ALICE), vec![2])); - assert_ok!(Farming::set_next_round_whitelist(RuntimeOrigin::signed(ALICE), vec![pid])); - assert_ok!(Farming::start_boost_round(RuntimeOrigin::signed(ALICE), 100)); - assert_ok!(Farming::charge_boost(RuntimeOrigin::signed(ALICE), charge_list)); - assert_noop!( - Farming::vote(RuntimeOrigin::signed(CHARLIE), vec![(2, Percent::from_percent(100))]), - Error::::NotInWhitelist - ); - assert_ok!(Farming::vote(RuntimeOrigin::signed(CHARLIE), vote_list)); - assert_eq!(Farming::boost_basic_rewards(pid, KSM), None); - assert_ok!(Farming::claim(RuntimeOrigin::signed(CHARLIE), pid)); - System::set_block_number(System::block_number() + 100); - Farming::on_initialize(System::block_number()); - System::set_block_number(System::block_number() + 1); - Farming::on_initialize(System::block_number()); - assert_ok!(Farming::claim(RuntimeOrigin::signed(CHARLIE), pid)); - assert_eq!(Farming::boost_basic_rewards(pid, KSM), Some(10)); - assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 2005); - assert_eq!(Tokens::free_balance(KSM, &CHARLIE), 1000000000005); - }); -} +use frame_support::{assert_err, assert_ok}; #[test] fn claim() { @@ -140,15 +63,15 @@ fn deposit() { assert_ok!(Farming::deposit(RuntimeOrigin::signed(ALICE), pid, tokens, Some((100, 100)))); System::set_block_number(System::block_number() + 1); assert_ok!(Farming::deposit(RuntimeOrigin::signed(ALICE), pid, 0, Some((100, 100)))); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 700); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 1000); let keeper: AccountId = ::Keeper::get().into_sub_account_truncating(pid); let reward_issuer: AccountId = ::RewardIssuer::get().into_sub_account_truncating(pid); let mut gauge_basic_rewards = BTreeMap::, BalanceOf>::new(); - gauge_basic_rewards.entry(KSM).or_insert(900); + gauge_basic_rewards.entry(KSM).or_insert(990_000); let gauge_pool_info2 = GaugePoolInfo { pid, - token: KSM, + token: Default::default(), keeper, reward_issuer, rewards: BTreeMap::< @@ -157,9 +80,9 @@ fn deposit() { >::new(), gauge_basic_rewards, max_block: 1000, - gauge_amount: 300, - total_time_factor: 89700, - gauge_last_block: System::block_number(), + gauge_amount: 0, + total_time_factor: 0, + gauge_last_block: 0, gauge_state: GaugeState::Bonded, }; assert_eq!(Farming::gauge_pool_infos(0), Some(gauge_pool_info2)); @@ -206,7 +129,7 @@ fn withdraw() { fn gauge() { ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { let (pid, tokens) = init_gauge(); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 1900); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 2000); if let Some(gauge_pool_infos) = Farming::gauge_pool_infos(0) { assert_eq!( gauge_pool_infos.rewards, @@ -220,25 +143,25 @@ fn gauge() { System::set_block_number(System::block_number() + 1); Farming::on_initialize(0); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 2918); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 3018); Farming::on_initialize(0); System::set_block_number(System::block_number() + 10); assert_ok!(Farming::deposit(RuntimeOrigin::signed(ALICE), pid, tokens, Some((100, 100)))); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 1818); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 2018); System::set_block_number(System::block_number() + 20); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 3163); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 3586); assert_ok!(Farming::deposit(RuntimeOrigin::signed(BOB), pid, 10, None)); assert_eq!(Tokens::free_balance(KSM, &BOB), 9699990); System::set_block_number(System::block_number() + 200); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 5383); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 7366); assert_eq!(Tokens::free_balance(KSM, &BOB), 9699990); assert_ok!(Farming::deposit(RuntimeOrigin::signed(BOB), pid, 0, Some((100, 100)))); System::set_block_number(System::block_number() + 200); assert_ok!(Farming::set_retire_limit(RuntimeOrigin::signed(ALICE), 10)); assert_ok!(Farming::force_gauge_claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &BOB), 9699991); + assert_eq!(Tokens::free_balance(KSM, &BOB), 9699990); }) } @@ -246,18 +169,18 @@ fn gauge() { fn gauge_withdraw() { ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { let (pid, _tokens) = init_gauge(); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 1900); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 2000); if let Some(gauge_pool_infos) = Farming::gauge_pool_infos(0) { - assert_eq!(gauge_pool_infos.gauge_amount, 100) + assert_eq!(gauge_pool_infos.gauge_amount, 0) }; Farming::on_initialize(0); System::set_block_number(System::block_number() + 1); Farming::on_initialize(0); assert_ok!(Farming::gauge_withdraw(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 1918); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 2018); System::set_block_number(System::block_number() + 1000); assert_ok!(Farming::gauge_withdraw(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 3782); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 21017); if let Some(gauge_pool_infos) = Farming::gauge_pool_infos(0) { assert_eq!(gauge_pool_infos.gauge_amount, 0) }; @@ -287,12 +210,12 @@ fn retire() { fn reset() { ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { let (pid, _tokens) = init_gauge(); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 1900); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 2000); Farming::on_initialize(0); System::set_block_number(System::block_number() + 1); Farming::on_initialize(0); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 2918); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 3018); assert_ok!(Farming::close_pool(RuntimeOrigin::signed(ALICE), pid)); assert_ok!(Farming::set_retire_limit(RuntimeOrigin::signed(ALICE), 10)); assert_ok!(Farming::force_retire_pool(RuntimeOrigin::signed(ALICE), pid)); @@ -306,7 +229,7 @@ fn reset() { None, None, None, - Some((KSM, 1000, basic_rewards)), + Some((1000, basic_rewards)), )); let keeper: AccountId = ::Keeper::get().into_sub_account_truncating(pid); let reward_issuer: AccountId = @@ -324,7 +247,7 @@ fn reset() { state: PoolState::UnCharged, keeper: keeper.clone(), reward_issuer: reward_issuer.clone(), - gauge: Some(1), + gauge: Some(0), block_startup: None, min_deposit_to_start: Default::default(), after_block_to_start: Default::default(), @@ -333,32 +256,16 @@ fn reset() { withdraw_limit_count: 5, }; assert_eq!(Farming::pool_infos(0), Some(pool_infos)); - let gauge_pool_info = GaugePoolInfo { - pid, - token: KSM, - keeper, - reward_issuer, - rewards: BTreeMap::< - CurrencyIdOf, - (BalanceOf, BalanceOf, BalanceOf), - >::new(), - gauge_basic_rewards: basic_rewards_map, - max_block: 1000, - gauge_amount: 0, - total_time_factor: 0, - gauge_last_block: System::block_number(), - gauge_state: GaugeState::Bonded, - }; - assert_eq!(Farming::gauge_pool_infos(1), Some(gauge_pool_info)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 3918); + assert_eq!(Farming::gauge_pool_infos(1), None); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 4018); let charge_rewards = vec![(KSM, 300000)]; - assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards)); + assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards, false)); assert_ok!(Farming::deposit(RuntimeOrigin::signed(ALICE), pid, 1, Some((100, 100)))); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 3817); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 4017); Farming::on_initialize(0); System::set_block_number(System::block_number() + 20); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 4017); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 4396); }) } @@ -368,13 +275,13 @@ fn init_gauge() -> (PoolId, BalanceOf) { let tokens_proportion = vec![(KSM, Perbill::from_percent(100))]; let tokens = 1000; let basic_rewards = vec![(KSM, 1000)]; - let gauge_basic_rewards = vec![(KSM, 900)]; + let gauge_basic_rewards = vec![(KSM, 990_000)]; assert_ok!(Farming::create_farming_pool( RuntimeOrigin::signed(ALICE), tokens_proportion.clone(), basic_rewards.clone(), - Some((KSM, 1000, gauge_basic_rewards)), + Some((1000, gauge_basic_rewards.clone())), 0, 0, 0, @@ -384,8 +291,20 @@ fn init_gauge() -> (PoolId, BalanceOf) { let pid = 0; let charge_rewards = vec![(KSM, 300000)]; - assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards)); + assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards, false)); assert_ok!(Farming::deposit(RuntimeOrigin::signed(ALICE), pid, tokens, Some((100, 100)))); + assert_ok!(VeMinting::set_config(RuntimeOrigin::signed(ALICE), Some(0), Some(7 * 86400 / 12))); + assert_ok!(VeMinting::notify_rewards( + RuntimeOrigin::signed(ALICE), + CHARLIE, + Some(7 * 86400 / 12), + gauge_basic_rewards.clone() + )); + assert_ok!(VeMinting::create_lock_inner( + &ALICE, + 100_000_000_000, + System::block_number() + 4 * 365 * 86400 / 12 + )); (pid, tokens) } @@ -410,7 +329,7 @@ fn init_no_gauge() -> (PoolId, BalanceOf) { let pid = 0; let charge_rewards = vec![(KSM, 100000)]; - assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards)); + assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards, false)); assert_ok!(Farming::deposit(RuntimeOrigin::signed(ALICE), pid, tokens, None)); (pid, tokens) } @@ -432,7 +351,7 @@ fn create_farming_pool() { RuntimeOrigin::signed(ALICE), tokens_proportion2, basic_rewards.clone(), - Some((KSM, 1000, gauge_basic_rewards.clone())), + Some((1000, gauge_basic_rewards.clone())), 2, 1, 7, @@ -445,7 +364,7 @@ fn create_farming_pool() { RuntimeOrigin::signed(ALICE), tokens_proportion.clone(), basic_rewards.clone(), - Some((KSM, 1000, gauge_basic_rewards.clone())), + Some((1000, gauge_basic_rewards.clone())), 2, 1, 7, @@ -456,7 +375,7 @@ fn create_farming_pool() { RuntimeOrigin::signed(ALICE), tokens_proportion.clone(), basic_rewards.clone(), - Some((KSM, 1000, gauge_basic_rewards)), + Some((1000, gauge_basic_rewards)), 2, 1, 7, @@ -470,7 +389,7 @@ fn create_farming_pool() { let pid = 1; let charge_rewards = vec![(KSM, 300000)]; - assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards)); + assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards, false)); if let Some(pool_infos) = Farming::pool_infos(0) { assert_eq!(pool_infos.total_shares, 0); assert_eq!(pool_infos.min_deposit_to_start, 2); @@ -495,10 +414,10 @@ fn create_farming_pool() { ); System::set_block_number(System::block_number() + 6); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 3008); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 3000); System::set_block_number(System::block_number() + 100); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 4698); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 3000); assert_ok!(Farming::withdraw(RuntimeOrigin::signed(ALICE), pid, Some(800))); System::set_block_number(System::block_number() + 6); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); @@ -506,9 +425,9 @@ fn create_farming_pool() { Farming::claim(RuntimeOrigin::signed(ALICE), pid), Error::::CanNotClaim ); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 4698); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 3000); System::set_block_number(System::block_number() + 6); assert_ok!(Farming::claim(RuntimeOrigin::signed(ALICE), pid)); - assert_eq!(Tokens::free_balance(KSM, &ALICE), 5498); + assert_eq!(Tokens::free_balance(KSM, &ALICE), 3800); }) } diff --git a/pallets/fee-share/src/mock.rs b/pallets/fee-share/src/mock.rs index cc5f93daed..ac61b7fdfe 100644 --- a/pallets/fee-share/src/mock.rs +++ b/pallets/fee-share/src/mock.rs @@ -323,6 +323,7 @@ parameter_types! { pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } impl bifrost_vtoken_minting::Config for Runtime { @@ -334,6 +335,7 @@ impl bifrost_vtoken_minting::Config for Runtime { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type BifrostSlp = Slp; type BifrostSlpx = SlpxInterface; type RelayChainToken = RelayCurrencyId; @@ -348,6 +350,9 @@ impl bifrost_vtoken_minting::Config for Runtime { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } parameter_types! { diff --git a/pallets/leverage-staking/src/mock.rs b/pallets/leverage-staking/src/mock.rs index 22a1945690..262ca66175 100644 --- a/pallets/leverage-staking/src/mock.rs +++ b/pallets/leverage-staking/src/mock.rs @@ -278,7 +278,7 @@ parameter_types! { pub const MaximumUnlockIdOfTimeUnit: u32 = 1_000; pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); - // pub BifrostFeeAccount: AccountId = 1.into(); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } pub struct SlpxInterface; @@ -301,6 +301,7 @@ impl bifrost_vtoken_minting::Config for Test { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = One; + type RedeemFeeAccount = One; type BifrostSlp = Slp; type RelayChainToken = RelayCurrencyId; type CurrencyIdConversion = AssetIdMaps; @@ -315,6 +316,9 @@ impl bifrost_vtoken_minting::Config for Test { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } pub struct Slp; diff --git a/pallets/salp/src/mock.rs b/pallets/salp/src/mock.rs index 98a8a2f3ad..ef22afb676 100644 --- a/pallets/salp/src/mock.rs +++ b/pallets/salp/src/mock.rs @@ -367,6 +367,7 @@ parameter_types! { pub const MaximumUnlockIdOfTimeUnit: u32 = 1_000; pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } pub struct SlpxInterface; @@ -421,6 +422,7 @@ impl bifrost_vtoken_minting::Config for Test { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = CouncilAccount; + type RedeemFeeAccount = CouncilAccount; type BifrostSlp = Slp; type RelayChainToken = RelayCurrencyId; type CurrencyIdConversion = AssetIdMaps; @@ -435,6 +437,9 @@ impl bifrost_vtoken_minting::Config for Test { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } parameter_types! { diff --git a/pallets/slp/src/mocks/mock.rs b/pallets/slp/src/mocks/mock.rs index 89b0922c92..991369d3c9 100644 --- a/pallets/slp/src/mocks/mock.rs +++ b/pallets/slp/src/mocks/mock.rs @@ -187,6 +187,7 @@ parameter_types! { pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } impl bifrost_vtoken_minting::Config for Runtime { @@ -198,6 +199,7 @@ impl bifrost_vtoken_minting::Config for Runtime { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type RelayChainToken = RelayCurrencyId; type CurrencyIdConversion = AssetIdMaps; type CurrencyIdRegister = AssetIdMaps; @@ -212,6 +214,9 @@ impl bifrost_vtoken_minting::Config for Runtime { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } parameter_types! { diff --git a/pallets/slp/src/mocks/mock_kusama.rs b/pallets/slp/src/mocks/mock_kusama.rs index 43ce4a7ff5..2264d78d28 100644 --- a/pallets/slp/src/mocks/mock_kusama.rs +++ b/pallets/slp/src/mocks/mock_kusama.rs @@ -241,6 +241,7 @@ parameter_types! { pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } pub struct SlpxInterface; @@ -259,6 +260,7 @@ impl bifrost_vtoken_minting::Config for Runtime { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type RelayChainToken = RelayCurrencyId; type CurrencyIdConversion = AssetIdMaps; type CurrencyIdRegister = AssetIdMaps; @@ -273,6 +275,9 @@ impl bifrost_vtoken_minting::Config for Runtime { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } parameter_types! { diff --git a/pallets/slpx/src/mock.rs b/pallets/slpx/src/mock.rs index f4193c27e3..8d2bd58764 100644 --- a/pallets/slpx/src/mock.rs +++ b/pallets/slpx/src/mock.rs @@ -170,6 +170,7 @@ parameter_types! { pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); pub const RelayCurrencyId: CurrencyId = KSM; + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } ord_parameter_types! { @@ -192,6 +193,7 @@ impl bifrost_vtoken_minting::Config for Test { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type RelayChainToken = RelayCurrencyId; type CurrencyIdConversion = AssetIdMaps; type CurrencyIdRegister = AssetIdMaps; @@ -206,6 +208,9 @@ impl bifrost_vtoken_minting::Config for Test { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } // Below is the implementation of tokens manipulation functions other than native token. pub struct LocalAssetAdaptor(PhantomData); diff --git a/pallets/stable-pool/src/mock.rs b/pallets/stable-pool/src/mock.rs index 8faef8191e..ac17939362 100644 --- a/pallets/stable-pool/src/mock.rs +++ b/pallets/stable-pool/src/mock.rs @@ -259,7 +259,7 @@ parameter_types! { pub const MaximumUnlockIdOfTimeUnit: u32 = 1_000; pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); - // pub BifrostFeeAccount: AccountId = 1.into(); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } pub struct SlpxInterface; @@ -282,6 +282,7 @@ impl bifrost_vtoken_minting::Config for Test { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = One; + type RedeemFeeAccount = One; type BifrostSlp = Slp; type RelayChainToken = RelayCurrencyId; type CurrencyIdConversion = AssetIdMaps; @@ -296,6 +297,9 @@ impl bifrost_vtoken_minting::Config for Test { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } pub struct Slp; diff --git a/pallets/system-maker/src/mock.rs b/pallets/system-maker/src/mock.rs index 87d36f4560..0bd9020431 100644 --- a/pallets/system-maker/src/mock.rs +++ b/pallets/system-maker/src/mock.rs @@ -330,6 +330,7 @@ parameter_types! { pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } impl bifrost_vtoken_minting::Config for Runtime { @@ -341,6 +342,7 @@ impl bifrost_vtoken_minting::Config for Runtime { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type BifrostSlp = Slp; type BifrostSlpx = SlpxInterface; type RelayChainToken = RelayCurrencyId; @@ -355,6 +357,9 @@ impl bifrost_vtoken_minting::Config for Runtime { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } parameter_types! { diff --git a/pallets/system-staking/src/mock.rs b/pallets/system-staking/src/mock.rs index 433ca2719d..6dbbc5b080 100644 --- a/pallets/system-staking/src/mock.rs +++ b/pallets/system-staking/src/mock.rs @@ -176,6 +176,7 @@ parameter_types! { pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); pub const RelayCurrencyId: CurrencyId = KSM; + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } impl bifrost_vtoken_minting::Config for Runtime { @@ -187,6 +188,7 @@ impl bifrost_vtoken_minting::Config for Runtime { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type BifrostSlp = Slp; type BifrostSlpx = SlpxInterface; type RelayChainToken = RelayCurrencyId; @@ -201,6 +203,9 @@ impl bifrost_vtoken_minting::Config for Runtime { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } ord_parameter_types! { @@ -322,6 +327,7 @@ parameter_types! { pub const FarmingRewardIssuerPalletId: PalletId = PalletId(*b"bf/fmrir"); pub const FarmingBoostPalletId: PalletId = PalletId(*b"bf/fmbst"); pub const WhitelistMaximumLimit: u32 = 10; + pub const FarmingGaugeRewardIssuerPalletId: PalletId = PalletId(*b"bf/fmgar"); } ord_parameter_types! { @@ -341,6 +347,7 @@ impl bifrost_farming::Config for Runtime { type VeMinting = (); type BlockNumberToBalance = ConvertInto; type WhitelistMaximumLimit = WhitelistMaximumLimit; + type GaugeRewardIssuer = FarmingGaugeRewardIssuerPalletId; } parameter_types! { diff --git a/pallets/system-staking/src/tests.rs b/pallets/system-staking/src/tests.rs index e58f3bb7c7..0af03cfce9 100644 --- a/pallets/system-staking/src/tests.rs +++ b/pallets/system-staking/src/tests.rs @@ -235,7 +235,7 @@ fn init_farming_no_gauge() -> (PoolId, BalanceOf) { RuntimeOrigin::signed(ALICE), tokens_proportion.clone(), basic_rewards.clone(), - Some((KSM, 1000, gauge_basic_rewards)), + Some((1000, gauge_basic_rewards)), 0, 0, 10, @@ -245,7 +245,7 @@ fn init_farming_no_gauge() -> (PoolId, BalanceOf) { let pid = 0; let charge_rewards = vec![(KSM, 100000)]; - assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards)); + assert_ok!(Farming::charge(RuntimeOrigin::signed(BOB), pid, charge_rewards, false)); assert_ok!(Farming::deposit(RuntimeOrigin::signed(ALICE), pid, tokens, None)); (pid, tokens) } diff --git a/pallets/ve-minting/Cargo.toml b/pallets/ve-minting/Cargo.toml index 7085304d3d..122c7a7fab 100644 --- a/pallets/ve-minting/Cargo.toml +++ b/pallets/ve-minting/Cargo.toml @@ -50,10 +50,11 @@ std = [ "frame-system/std", "frame-benchmarking/std", "bifrost-primitives/std", - "orml-traits/std", "bifrost-slp/std", "bifrost-asset-registry/std", "bifrost-runtime-common/std", + "orml-traits/std", + "orml-xtokens/std", ] runtime-benchmarks = [ diff --git a/pallets/ve-minting/src/benchmarking.rs b/pallets/ve-minting/src/benchmarking.rs index 12032e855c..93822ced84 100644 --- a/pallets/ve-minting/src/benchmarking.rs +++ b/pallets/ve-minting/src/benchmarking.rs @@ -76,7 +76,7 @@ benchmarks! { (365 * 86400 / 12u32).into() )); - }: _(RawOrigin::Signed(test_account),BalanceOf::::unique_saturated_from(50000000000u128)) + }: _(RawOrigin::Signed(test_account),0,BalanceOf::::unique_saturated_from(50000000000u128)) increase_unlock_time { let test_account: T::AccountId = account("seed",1,1); @@ -100,7 +100,7 @@ benchmarks! { (365 * 86400 / 12u32).into() )); - }: _(RawOrigin::Signed(test_account),(7 * 86400 / 12u32 + 365 * 86400 / 12u32).into()) + }: _(RawOrigin::Signed(test_account),0,(7 * 86400 / 12u32 + 365 * 86400 / 12u32).into()) withdraw { let test_account: T::AccountId = account("seed",1,1); @@ -126,7 +126,7 @@ benchmarks! { >::set_block_number((2 * 365 * 86400 / 12u32).into()); - }: _(RawOrigin::Signed(test_account)) + }: _(RawOrigin::Signed(test_account),0) get_rewards { let test_account: T::AccountId = account("seed",1,1); @@ -154,7 +154,6 @@ benchmarks! { }: _(RawOrigin::Signed(test_account)) - notify_rewards { assert_ok!(VeMinting::::set_config( RawOrigin::Root.into(), @@ -165,5 +164,141 @@ benchmarks! { T::MultiCurrency::deposit(CurrencyId::Native(TokenSymbol::BNC), &account("seed",1,1), BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; }: _(RawOrigin::Root,account("seed",1,1),Some((7 * 86400 / 12u32).into()),rewards) + set_markup_coefficient { + assert_ok!(VeMinting::::set_config( + RawOrigin::Root.into(), + Some((4 * 365 * 86400 / 12u32).into()), + Some((7 * 86400 / 12u32).into()) + )); + T::MultiCurrency::deposit(CurrencyId::Native(TokenSymbol::BNC), &account("seed",1,1), BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + }: _(RawOrigin::Root, CurrencyId::VToken(TokenSymbol::BNC), 10_000.into(), 10_000_000_000_000.into()) + + deposit_markup { + let test_account: T::AccountId = account("seed",1,1); + assert_ok!(VeMinting::::set_config( + RawOrigin::Root.into(), + Some((4 * 365 * 86400 / 12u32).into()), + Some((7 * 86400 / 12u32).into()) + )); + T::MultiCurrency::deposit(CurrencyId::Native(TokenSymbol::BNC), &test_account, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + T::MultiCurrency::deposit(CurrencyId::VToken(TokenSymbol::BNC), &test_account, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + let rewards = vec![(CurrencyId::Native(TokenSymbol::BNC), BalanceOf::::unique_saturated_from(10_000_000_000_000u128))]; + assert_ok!(VeMinting::::notify_rewards( + T::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + account("seed",1,1), + Some((7 * 86400 / 12u32).into()),rewards + )); + + assert_ok!(VeMinting::::create_lock( + RawOrigin::Signed(test_account.clone()).into(), + BalanceOf::::unique_saturated_from(10_000_000_000_000u128), + (365 * 86400 / 12u32).into() + )); + assert_ok!(VeMinting::::set_markup_coefficient( + RawOrigin::Root.into(), + CurrencyId::VToken(TokenSymbol::BNC), + 1_000.into(), + 10_000_000_000_000.into() + )); + >::set_block_number((2 * 365 * 86400 / 12u32).into()); + + }: _(RawOrigin::Signed(test_account), CurrencyId::VToken(TokenSymbol::BNC), BalanceOf::::unique_saturated_from(10_000_000_000_000u128)) + + withdraw_markup { + let test_account: T::AccountId = account("seed",1,1); + assert_ok!(VeMinting::::set_config( + RawOrigin::Root.into(), + Some((4 * 365 * 86400 / 12u32).into()), + Some((7 * 86400 / 12u32).into()) + )); + T::MultiCurrency::deposit(CurrencyId::Native(TokenSymbol::BNC), &test_account, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + T::MultiCurrency::deposit(CurrencyId::VToken(TokenSymbol::BNC), &test_account, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + let rewards = vec![(CurrencyId::Native(TokenSymbol::BNC), BalanceOf::::unique_saturated_from(10_000_000_000_000u128))]; + assert_ok!(VeMinting::::notify_rewards( + T::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + account("seed",1,1), + Some((7 * 86400 / 12u32).into()),rewards + )); + + assert_ok!(VeMinting::::create_lock( + RawOrigin::Signed(test_account.clone()).into(), + BalanceOf::::unique_saturated_from(10_000_000_000_000u128), + (365 * 86400 / 12u32).into() + )); + assert_ok!(VeMinting::::set_markup_coefficient( + RawOrigin::Root.into(), + CurrencyId::VToken(TokenSymbol::BNC), + 1_000.into(), + 10_000_000_000_000.into() + )); + assert_ok!(VeMinting::::deposit_markup(RawOrigin::Signed(test_account.clone()).into(), CurrencyId::VToken(TokenSymbol::BNC), BalanceOf::::unique_saturated_from(10_000_000_000_000u128))); + >::set_block_number((2 * 365 * 86400 / 12u32).into()); + + }: _(RawOrigin::Signed(test_account), CurrencyId::VToken(TokenSymbol::BNC)) + + redeem_unlock { + let test_account: T::AccountId = account("seed",1,1); + assert_ok!(VeMinting::::set_config( + RawOrigin::Root.into(), + Some((4 * 365 * 86400 / 12u32).into()), + Some((7 * 86400 / 12u32).into()) + )); + T::MultiCurrency::deposit(CurrencyId::Native(TokenSymbol::BNC), &test_account, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + T::MultiCurrency::deposit(CurrencyId::VToken(TokenSymbol::BNC), &test_account, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + let rewards = vec![(CurrencyId::Native(TokenSymbol::BNC), BalanceOf::::unique_saturated_from(10_000_000_000_000u128))]; + assert_ok!(VeMinting::::notify_rewards( + T::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + account("seed",1,1), + Some((7 * 86400 / 12u32).into()),rewards + )); + + assert_ok!(VeMinting::::create_lock( + RawOrigin::Signed(test_account.clone()).into(), + BalanceOf::::unique_saturated_from(10_000_000_000_000u128), + (365 * 86400 / 12u32).into() + )); + assert_ok!(VeMinting::::set_markup_coefficient( + RawOrigin::Root.into(), + CurrencyId::VToken(TokenSymbol::BNC), + 1_000.into(), + 10_000_000_000_000.into() + )); + assert_ok!(VeMinting::::deposit_markup(RawOrigin::Signed(test_account.clone()).into(), CurrencyId::VToken(TokenSymbol::BNC), BalanceOf::::unique_saturated_from(10_000_000_000_000u128))); + >::set_block_number((2 * 86400 / 12u32).into()); + + }: _(RawOrigin::Signed(test_account), 0) + + refresh { + let test_account: T::AccountId = account("seed",1,1); + assert_ok!(VeMinting::::set_config( + RawOrigin::Root.into(), + Some((4 * 365 * 86400 / 12u32).into()), + Some((7 * 86400 / 12u32).into()) + )); + T::MultiCurrency::deposit(CurrencyId::Native(TokenSymbol::BNC), &test_account, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + T::MultiCurrency::deposit(CurrencyId::VToken(TokenSymbol::BNC), &test_account, BalanceOf::::unique_saturated_from(100_000_000_000_000u128))?; + let rewards = vec![(CurrencyId::Native(TokenSymbol::BNC), BalanceOf::::unique_saturated_from(10_000_000_000_000u128))]; + assert_ok!(VeMinting::::notify_rewards( + T::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, + account("seed",1,1), + Some((7 * 86400 / 12u32).into()),rewards + )); + + assert_ok!(VeMinting::::create_lock( + RawOrigin::Signed(test_account.clone()).into(), + BalanceOf::::unique_saturated_from(10_000_000_000_000u128), + (365 * 86400 / 12u32).into() + )); + assert_ok!(VeMinting::::set_markup_coefficient( + RawOrigin::Root.into(), + CurrencyId::VToken(TokenSymbol::BNC), + 1_000.into(), + 10_000_000_000_000.into() + )); + assert_ok!(VeMinting::::deposit_markup(RawOrigin::Signed(test_account.clone()).into(), CurrencyId::VToken(TokenSymbol::BNC), BalanceOf::::unique_saturated_from(10_000_000_000_000u128))); + >::set_block_number((2 * 86400 / 12u32).into()); + + }: _(RawOrigin::Signed(test_account), CurrencyId::VToken(TokenSymbol::BNC)) + impl_benchmark_test_suite!(VeMinting,crate::mock::ExtBuilder::default().build(),crate::mock::Runtime); } diff --git a/pallets/ve-minting/src/incentive.rs b/pallets/ve-minting/src/incentive.rs index 8cc34e7581..5af370683a 100644 --- a/pallets/ve-minting/src/incentive.rs +++ b/pallets/ve-minting/src/incentive.rs @@ -16,34 +16,56 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{ - traits::{Incentive, VeMintingInterface}, - *, -}; +use crate::{traits::VeMintingInterface, *}; +use bifrost_primitives::PoolId; pub use pallet::*; use sp_std::collections::btree_map::BTreeMap; -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Default)] -pub struct IncentiveConfig { +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct IncentiveConfig { pub reward_rate: BTreeMap, pub reward_per_token_stored: BTreeMap, pub rewards_duration: BlockNumber, pub period_finish: BlockNumber, pub last_update_time: BlockNumber, + pub incentive_controller: Option, + pub last_reward: Vec<(CurrencyId, Balance)>, +} + +impl Default + for IncentiveConfig +where + CurrencyId: Default, + Balance: Default, + BlockNumber: Default, +{ + fn default() -> Self { + IncentiveConfig { + reward_rate: Default::default(), + reward_per_token_stored: Default::default(), + rewards_duration: Default::default(), + period_finish: Default::default(), + last_update_time: Default::default(), + incentive_controller: None, + last_reward: Default::default(), + } + } } impl Pallet { - pub fn last_time_reward_applicable() -> BlockNumberFor { + pub fn last_time_reward_applicable(pool_id: PoolId) -> BlockNumberFor { let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); - if current_block_number < Self::incentive_configs().period_finish { + if current_block_number < Self::incentive_configs(pool_id).period_finish { current_block_number } else { - Self::incentive_configs().period_finish + Self::incentive_configs(pool_id).period_finish } } - pub fn reward_per_token() -> Result, BalanceOf>, DispatchError> { - let mut conf = Self::incentive_configs(); + pub fn reward_per_token( + pool_id: PoolId, + ) -> Result, BalanceOf>, DispatchError> { + let mut conf = Self::incentive_configs(pool_id); let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); let total_supply = Self::total_supply(current_block_number)?; if total_supply == BalanceOf::::zero() { @@ -51,7 +73,7 @@ impl Pallet { } conf.reward_rate.iter().try_for_each(|(currency, &reward)| -> DispatchResult { let increment: BalanceOf = U512::from( - Self::last_time_reward_applicable() + Self::last_time_reward_applicable(pool_id) .saturating_sub(conf.last_update_time) .saturated_into::(), ) @@ -71,14 +93,16 @@ impl Pallet { Ok(()) })?; - IncentiveConfigs::::set(conf.clone()); + IncentiveConfigs::::set(pool_id, conf.clone()); Ok(conf.reward_per_token_stored) } pub fn earned( + pool_id: PoolId, addr: &AccountIdOf, + share_info: Option<(BalanceOf, BalanceOf)>, ) -> Result, BalanceOf>, DispatchError> { - let reward_per_token = Self::reward_per_token()?; + let reward_per_token = Self::reward_per_token(pool_id)?; let vetoken_balance = Self::balance_of_current_block(addr)?; let mut rewards = if let Some(rewards) = Self::rewards(addr) { rewards @@ -86,7 +110,7 @@ impl Pallet { BTreeMap::, BalanceOf>::default() }; reward_per_token.iter().try_for_each(|(currency, reward)| -> DispatchResult { - let increment: BalanceOf = U256::from(vetoken_balance.saturated_into::()) + let increment = U256::from(vetoken_balance.saturated_into::()) .checked_mul(U256::from( reward .saturating_sub( @@ -98,16 +122,41 @@ impl Pallet { )) .ok_or(ArithmeticError::Overflow)? .checked_div(U256::from(T::Multiplier::get().saturated_into::())) - .map(|x| u128::try_from(x)) - .ok_or(ArithmeticError::Overflow)? - .map_err(|_| ArithmeticError::Overflow)? - .unique_saturated_into(); - rewards - .entry(*currency) - .and_modify(|total_reward| { - *total_reward = total_reward.saturating_add(increment); - }) - .or_insert(increment); + .ok_or(ArithmeticError::Overflow)?; + // .map(|x| u128::try_from(x)) + // .ok_or(ArithmeticError::Overflow)? + // .map_err(|_| ArithmeticError::Overflow)? + // .unique_saturated_into(); + + match share_info { + Some((share, total_share)) => { + let reward = increment + .checked_mul(U256::from(share.saturated_into::())) + .ok_or(ArithmeticError::Overflow)? + .checked_div(U256::from(total_share.saturated_into::())) + .map(|x| u128::try_from(x)) + .ok_or(ArithmeticError::Overflow)? + .map_err(|_| ArithmeticError::Overflow)? + .unique_saturated_into(); + rewards + .entry(*currency) + .and_modify(|total_reward| { + *total_reward = total_reward.saturating_add(reward); + }) + .or_insert(reward); + }, + None => { + let reward = u128::try_from(increment) + .map_err(|_| ArithmeticError::Overflow)? + .unique_saturated_into(); + rewards + .entry(*currency) + .and_modify(|total_reward| { + *total_reward = total_reward.saturating_add(reward); + }) + .or_insert(reward); + }, + } Ok(()) })?; Ok(rewards) @@ -115,15 +164,19 @@ impl Pallet { // Used to update reward when notify_reward or user call // create_lock/increase_amount/increase_unlock_time/withdraw/get_rewards - pub fn update_reward(addr: Option<&AccountIdOf>) -> DispatchResult { - let reward_per_token_stored = Self::reward_per_token()?; + pub fn update_reward( + pool_id: PoolId, + addr: Option<&AccountIdOf>, + share_info: Option<(BalanceOf, BalanceOf)>, + ) -> DispatchResult { + let reward_per_token_stored = Self::reward_per_token(pool_id)?; - IncentiveConfigs::::mutate(|item| { + IncentiveConfigs::::mutate(pool_id, |item| { item.reward_per_token_stored = reward_per_token_stored.clone(); - item.last_update_time = Self::last_time_reward_applicable(); + item.last_update_time = Self::last_time_reward_applicable(pool_id); }); if let Some(address) = addr { - let earned = Self::earned(&address)?; + let earned = Self::earned(pool_id, address, share_info)?; if earned != BTreeMap::, BalanceOf>::default() { Rewards::::insert(address, earned); } @@ -132,9 +185,21 @@ impl Pallet { Ok(()) } - pub fn get_rewards_inner(addr: &AccountIdOf) -> DispatchResult { - Self::update_reward(Some(addr))?; + pub fn update_reward_all(addr: Option<&AccountIdOf>) -> DispatchResult { + // TODO: pool_id + Self::update_reward(0, addr, None)?; + Ok(()) + } + pub fn get_rewards_inner( + pool_id: PoolId, + addr: &AccountIdOf, + share_info: Option<(BalanceOf, BalanceOf)>, + ) -> DispatchResult { + Self::update_reward(pool_id, Some(addr), share_info)?; + if Self::balance_of_current_block(addr)? == BalanceOf::::zero() { + return Ok(()); + } if let Some(rewards) = Self::rewards(addr) { rewards.iter().try_for_each(|(currency, &reward)| -> DispatchResult { T::MultiCurrency::transfer( @@ -157,25 +222,32 @@ impl Pallet { // Motion pub fn notify_reward_amount( - addr: &AccountIdOf, + pool_id: PoolId, + addr: &Option>, rewards: Vec<(CurrencyIdOf, BalanceOf)>, ) -> DispatchResult { - Self::update_reward(None)?; - let mut conf = Self::incentive_configs(); + let who = match addr { + Some(addr) => addr, + None => return Err(Error::::NoController.into()), + }; + Self::update_reward(pool_id, None, None)?; + let mut conf = Self::incentive_configs(pool_id); let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); if current_block_number >= conf.period_finish { - Self::add_reward(addr, &mut conf, &rewards, Zero::zero())?; + Self::add_reward(who, &mut conf, &rewards, Zero::zero())?; } else { let remaining = T::BlockNumberToBalance::convert( conf.period_finish.saturating_sub(current_block_number), ); - Self::add_reward(addr, &mut conf, &rewards, remaining)?; + Self::add_reward(who, &mut conf, &rewards, remaining)?; }; conf.last_update_time = current_block_number; conf.period_finish = current_block_number.saturating_add(conf.rewards_duration); - IncentiveConfigs::::set(conf); + conf.incentive_controller = Some(who.clone()); + conf.last_reward = rewards.clone(); + IncentiveConfigs::::set(pool_id, conf); Self::deposit_event(Event::RewardAdded { rewards }); Ok(()) diff --git a/pallets/ve-minting/src/lib.rs b/pallets/ve-minting/src/lib.rs index a91d03bb65..e6c6a2b113 100644 --- a/pallets/ve-minting/src/lib.rs +++ b/pallets/ve-minting/src/lib.rs @@ -32,8 +32,7 @@ pub mod incentive; pub mod traits; pub mod weights; -use crate::traits::Incentive; -use bifrost_primitives::CurrencyId; +use bifrost_primitives::{Balance, CurrencyId, PoolId}; use frame_support::{ pallet_prelude::*, sp_runtime::{ @@ -41,16 +40,19 @@ use frame_support::{ AccountIdConversion, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Convert, Saturating, UniqueSaturatedInto, Zero, }, - ArithmeticError, DispatchError, SaturatedConversion, + ArithmeticError, DispatchError, FixedU128, Perbill, SaturatedConversion, }, PalletId, }; use frame_system::pallet_prelude::*; pub use incentive::*; -use orml_traits::{MultiCurrency, MultiLockableCurrency}; +use orml_traits::{LockIdentifier, MultiCurrency, MultiLockableCurrency}; +pub use pallet::*; use sp_core::{U256, U512}; -use sp_std::{borrow::ToOwned, collections::btree_map::BTreeMap, vec, vec::Vec}; -pub use traits::VeMintingInterface; +use sp_std::{borrow::ToOwned, cmp::Ordering, collections::btree_map::BTreeMap, vec, vec::Vec}; +pub use traits::{ + LockedToken, MarkupCoefficientInfo, MarkupInfo, UserMarkupInfo, VeMintingInterface, +}; pub use weights::WeightInfo; type BalanceOf = <::MultiCurrency as MultiCurrency>>::Balance; @@ -61,6 +63,9 @@ pub type CurrencyIdOf = <::MultiCurrency as MultiCurrency< ::AccountId, >>::CurrencyId; +const VE_LOCK_ID: LockIdentifier = *b"vebnclck"; +const MARKUP_LOCK_ID: LockIdentifier = *b"vebncmkp"; + #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Default)] pub struct VeConfig { amount: Balance, @@ -94,7 +99,7 @@ pub mod pallet { pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type MultiCurrency: MultiCurrency, CurrencyId = CurrencyId> + type MultiCurrency: MultiCurrency, CurrencyId = CurrencyId, Balance = Balance> + MultiLockableCurrency, CurrencyId = CurrencyId>; type ControlOrigin: EnsureOrigin; @@ -124,6 +129,13 @@ pub mod pallet { #[pallet::constant] type VoteWeightMultiplier: Get>; + + /// The maximum number of positions that should exist on an account. + #[pallet::constant] + type MaxPositions: Get; + + #[pallet::constant] + type MarkupRefreshLimit: Get; } #[pallet::event] @@ -133,7 +145,7 @@ pub mod pallet { config: VeConfig, BlockNumberFor>, }, Minted { - addr: AccountIdOf, + addr: u128, value: BalanceOf, end: BlockNumberFor, now: BlockNumberFor, @@ -148,19 +160,21 @@ pub mod pallet { unlock_time: BlockNumberFor, }, UnlockTimeIncreased { - addr: AccountIdOf, + addr: u128, unlock_time: BlockNumberFor, }, AmountIncreased { - addr: AccountIdOf, + who: AccountIdOf, + position: u128, value: BalanceOf, }, Withdrawn { - addr: AccountIdOf, + addr: u128, value: BalanceOf, }, IncentiveSet { - rewards_duration: BlockNumberFor, + incentive_config: + IncentiveConfig, BalanceOf, BlockNumberFor, AccountIdOf>, }, RewardAdded { rewards: Vec<(CurrencyIdOf, BalanceOf)>, @@ -169,6 +183,12 @@ pub mod pallet { addr: AccountIdOf, rewards: Vec<(CurrencyIdOf, BalanceOf)>, }, + AllRefreshed { + asset_id: CurrencyIdOf, + }, + PartiallyRefreshed { + asset_id: CurrencyIdOf, + }, } #[pallet::error] @@ -179,6 +199,9 @@ pub mod pallet { LockNotExist, LockExist, NoRewards, + ArgumentsError, + ExceedsMaxPositions, + NoController, } #[pallet::storage] @@ -199,11 +222,22 @@ pub mod pallet { pub type Locked = StorageMap< _, Blake2_128Concat, - AccountIdOf, + u128, LockedBalance, BlockNumberFor>, ValueQuery, >; + #[pallet::storage] + #[pallet::getter(fn user_locked)] + pub type UserLocked = StorageMap< + _, + Blake2_128Concat, + AccountIdOf, + BalanceOf, + // LockedBalance, BlockNumberFor>, + ValueQuery, + >; + // Each week has a Point struct stored in PointHistory. #[pallet::storage] #[pallet::getter(fn point_history)] @@ -215,7 +249,7 @@ pub mod pallet { pub type UserPointHistory = StorageDoubleMap< _, Blake2_128Concat, - AccountIdOf, + u128, Blake2_128Concat, U256, Point, BlockNumberFor>, @@ -224,8 +258,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn user_point_epoch)] - pub type UserPointEpoch = - StorageMap<_, Blake2_128Concat, AccountIdOf, U256, ValueQuery>; + pub type UserPointEpoch = StorageMap<_, Blake2_128Concat, u128, U256, ValueQuery>; #[pallet::storage] #[pallet::getter(fn slope_changes)] @@ -235,9 +268,11 @@ pub mod pallet { // Incentive #[pallet::storage] #[pallet::getter(fn incentive_configs)] - pub type IncentiveConfigs = StorageValue< + pub type IncentiveConfigs = StorageMap< _, - IncentiveConfig, BalanceOf, BlockNumberFor>, + Blake2_128Concat, + PoolId, + IncentiveConfig, BalanceOf, BlockNumberFor, AccountIdOf>, ValueQuery, >; @@ -256,6 +291,57 @@ pub mod pallet { pub type Rewards = StorageMap<_, Blake2_128Concat, AccountIdOf, BTreeMap, BalanceOf>>; + #[pallet::storage] + #[pallet::getter(fn user_markup_infos)] + pub type UserMarkupInfos = + StorageMap<_, Blake2_128Concat, AccountIdOf, UserMarkupInfo>; + + #[pallet::storage] + #[pallet::getter(fn locked_tokens)] + pub type LockedTokens = StorageDoubleMap< + _, + Blake2_128Concat, + CurrencyIdOf, + Blake2_128Concat, + AccountIdOf, + LockedToken, BlockNumberFor>, + >; + + #[pallet::storage] + #[pallet::getter(fn total_lock)] + pub type TotalLock = + StorageMap<_, Twox64Concat, CurrencyIdOf, BalanceOf, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn markup_coefficient)] + pub type MarkupCoefficient = + StorageMap<_, Twox64Concat, CurrencyIdOf, MarkupCoefficientInfo>>; + + #[pallet::storage] + #[pallet::getter(fn position)] + pub type Position = StorageValue<_, u128, ValueQuery>; + + #[pallet::storage] + pub type UserPositions = StorageMap< + _, + Blake2_128Concat, + AccountIdOf, + BoundedVec, + ValueQuery, + >; + + // #[pallet::hooks] + // impl Hooks> for Pallet { + // fn on_initialize(n: BlockNumberFor) -> Weight { + // let conf = Self::incentive_configs(); + // if n == conf.last_update_time + conf.rewards_duration { + // Self::notify_reward_amount(&conf.incentive_controller, conf.last_reward.clone()) + // .unwrap_or_default(); + // } + // T::DbWeight::get().writes(1_u64) + // } + // } + #[pallet::call] impl Pallet { #[pallet::call_index(0)] @@ -287,32 +373,44 @@ pub mod pallet { value: BalanceOf, unlock_time: BlockNumberFor, ) -> DispatchResult { - let exchanger: AccountIdOf = ensure_signed(origin)?; + let exchanger = ensure_signed(origin)?; Self::create_lock_inner(&exchanger, value, unlock_time) } #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::increase_amount())] - pub fn increase_amount(origin: OriginFor, value: BalanceOf) -> DispatchResult { - let exchanger: AccountIdOf = ensure_signed(origin)?; - Self::increase_amount_inner(&exchanger, value) + pub fn increase_amount( + origin: OriginFor, + position: u128, + value: BalanceOf, + ) -> DispatchResult { + let exchanger = ensure_signed(origin)?; + // TODO: ensure postion is owned by exchanger + let user_positions = UserPositions::::get(&exchanger); + ensure!(user_positions.contains(&position), Error::::LockNotExist); + Self::increase_amount_inner(&exchanger, position, value) } #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::increase_unlock_time())] pub fn increase_unlock_time( origin: OriginFor, + position: u128, time: BlockNumberFor, ) -> DispatchResult { let exchanger = ensure_signed(origin)?; - Self::increase_unlock_time_inner(&exchanger, time) + let user_positions = UserPositions::::get(&exchanger); + ensure!(user_positions.contains(&position), Error::::LockNotExist); + Self::increase_unlock_time_inner(&exchanger, position, time) } #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::withdraw())] - pub fn withdraw(origin: OriginFor) -> DispatchResult { + pub fn withdraw(origin: OriginFor, position: u128) -> DispatchResult { let exchanger = ensure_signed(origin)?; - Self::withdraw_inner(&exchanger) + let user_positions = UserPositions::::get(&exchanger); + ensure!(user_positions.contains(&position), Error::::LockNotExist); + Self::withdraw_inner(&exchanger, position) } #[pallet::call_index(5)] @@ -324,25 +422,81 @@ pub mod pallet { rewards: Vec<(CurrencyIdOf, BalanceOf)>, ) -> DispatchResult { T::ControlOrigin::ensure_origin(origin)?; - Self::set_incentive(rewards_duration); - Self::notify_reward_amount(&incentive_from, rewards) + Self::set_incentive(0, rewards_duration, Some(incentive_from.clone())); // for pool0 + Self::notify_reward_amount(0, &Some(incentive_from), rewards) // for pool0 } #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::get_rewards())] pub fn get_rewards(origin: OriginFor) -> DispatchResult { let exchanger = ensure_signed(origin)?; - Self::get_rewards_inner(&exchanger) + Self::get_rewards_inner(0, &exchanger, None) // for pool0 + } + + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::redeem_unlock())] + pub fn redeem_unlock(origin: OriginFor, position: u128) -> DispatchResult { + let exchanger = ensure_signed(origin)?; + Self::redeem_unlock_inner(&exchanger, position) + } + + #[pallet::call_index(8)] + #[pallet::weight(T::WeightInfo::set_markup_coefficient())] + pub fn set_markup_coefficient( + origin: OriginFor, + asset_id: CurrencyId, // tokenη±»εž‹ + markup: FixedU128, // 单位tokenηš„εŠ ζˆη³»ζ•° + hardcap: FixedU128, // tokenε―ΉεΊ”εŠ ζˆη‘¬ι‘Ά + ) -> DispatchResult { + T::ControlOrigin::ensure_origin(origin)?; + ensure!(markup <= hardcap, Error::::ArgumentsError); + + if !TotalLock::::contains_key(asset_id) { + TotalLock::::insert(asset_id, BalanceOf::::zero()); + } + let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); + MarkupCoefficient::::insert( + asset_id, + MarkupCoefficientInfo { + markup_coefficient: markup, + hardcap, + update_block: current_block_number, + }, + ); + Ok(()) + } + + #[pallet::call_index(9)] + #[pallet::weight(T::WeightInfo::deposit_markup())] + pub fn deposit_markup( + origin: OriginFor, + asset_id: CurrencyIdOf, + value: BalanceOf, + ) -> DispatchResult { + Self::deposit_markup_inner(origin, asset_id, value) + } + + #[pallet::call_index(10)] + #[pallet::weight(T::WeightInfo::withdraw_markup())] + pub fn withdraw_markup(origin: OriginFor, asset_id: CurrencyIdOf) -> DispatchResult { + Self::withdraw_markup_inner(origin, asset_id) + } + + #[pallet::call_index(11)] + #[pallet::weight(T::WeightInfo::refresh())] + pub fn refresh(origin: OriginFor, asset_id: CurrencyIdOf) -> DispatchResult { + Self::refresh_inner(origin, asset_id) } } impl Pallet { pub fn _checkpoint( - addr: &AccountIdOf, + who: &AccountIdOf, + addr: u128, old_locked: LockedBalance, BlockNumberFor>, new_locked: LockedBalance, BlockNumberFor>, ) -> DispatchResult { - Self::update_reward(Some(addr))?; + Self::update_reward_all(Some(who))?; let mut u_old = Point::, BlockNumberFor>::default(); let mut u_new = Point::, BlockNumberFor>::default(); @@ -398,10 +552,11 @@ pub mod pallet { if g_epoch > U256::zero() { last_point = Self::point_history(g_epoch); } else { - last_point.amount = T::MultiCurrency::free_balance( - T::TokenType::get(), - &T::VeMintingPalletId::get().into_account_truncating(), - ); + // last_point.amount = T::MultiCurrency::free_balance( + // T::TokenType::get(), + // &T::VeMintingPalletId::get().into_account_truncating(), + // ); + last_point.amount = Self::supply(); } let mut last_checkpoint = last_point.block; let mut t_i: BlockNumberFor = last_checkpoint @@ -449,10 +604,11 @@ pub mod pallet { // Fill for the current block, if applicable if t_i == current_block_number { - last_point.amount = T::MultiCurrency::free_balance( - T::TokenType::get(), - &T::VeMintingPalletId::get().into_account_truncating(), - ); + last_point.amount = Self::supply(); + // last_point.amount = T::MultiCurrency::free_balance( + // T::TokenType::get(), + // &T::VeMintingPalletId::get().into_account_truncating(), + // ); break; } else { PointHistory::::insert(g_epoch, last_point); @@ -506,14 +662,16 @@ pub mod pallet { .ok_or(ArithmeticError::Overflow)?; UserPointEpoch::::insert(addr, user_epoch); u_new.block = current_block_number; - u_new.amount = Self::locked(addr).amount; + // u_new.amount = Self::locked(addr).amount; + u_new.amount = new_locked.amount; UserPointHistory::::insert(addr, user_epoch, u_new); Ok(()) } pub fn _deposit_for( - addr: &AccountIdOf, + who: &AccountIdOf, + addr: u128, value: BalanceOf, unlock_time: BlockNumberFor, locked_balance: LockedBalance, BlockNumberFor>, @@ -521,41 +679,52 @@ pub mod pallet { let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); let mut _locked = locked_balance; let supply_before = Self::supply(); - Supply::::set(supply_before.checked_add(&value).ok_or(ArithmeticError::Overflow)?); + Supply::::set(supply_before.checked_add(value).ok_or(ArithmeticError::Overflow)?); let old_locked = _locked.clone(); - _locked.amount = _locked.amount.checked_add(&value).ok_or(ArithmeticError::Overflow)?; + _locked.amount = _locked.amount.checked_add(value).ok_or(ArithmeticError::Overflow)?; if unlock_time != Zero::zero() { _locked.end = unlock_time } Locked::::insert(addr, _locked.clone()); - if value != BalanceOf::::zero() { - T::MultiCurrency::transfer( + let free_balance = T::MultiCurrency::free_balance(T::TokenType::get(), &who); + if value != BalanceOf::::zero() && value <= free_balance { + let new_locked_balance = UserLocked::::get(who) + .checked_add(value) + .ok_or(ArithmeticError::Underflow)?; + T::MultiCurrency::set_lock( + VE_LOCK_ID, T::TokenType::get(), - addr, - &T::VeMintingPalletId::get().into_account_truncating(), - value, + who, + new_locked_balance, )?; + UserLocked::::set(who, new_locked_balance); } - Self::_checkpoint(addr, old_locked, _locked.clone())?; + Self::markup_calc( + who, + addr, + old_locked, + _locked.clone(), + UserMarkupInfos::::get(who).as_ref(), + )?; Self::deposit_event(Event::Minted { - addr: addr.clone(), + addr, value, end: _locked.end, now: current_block_number, }); Self::deposit_event(Event::Supply { supply_before, - supply: supply_before.checked_add(&value).ok_or(ArithmeticError::Overflow)?, + supply: supply_before.checked_add(value).ok_or(ArithmeticError::Overflow)?, }); Ok(()) } // Get the current voting power for `addr` - pub(crate) fn balance_of_current_block( - addr: &AccountIdOf, + pub(crate) fn balance_of_position_current_block( + addr: u128, ) -> Result, DispatchError> { let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); let u_epoch = Self::user_point_epoch(addr); @@ -583,20 +752,15 @@ pub mod pallet { last_point.bias = 0_i128 } - Ok(last_point - .amount - .checked_add( - &T::VoteWeightMultiplier::get() - .checked_mul(&(last_point.bias as u128).unique_saturated_into()) - .ok_or(ArithmeticError::Overflow)?, - ) + Ok(T::VoteWeightMultiplier::get() + .checked_mul((last_point.bias as u128).unique_saturated_into()) .ok_or(ArithmeticError::Overflow)?) } } // Measure voting power of `addr` at block height `block` - pub(crate) fn balance_of_at( - addr: &AccountIdOf, + pub(crate) fn balance_of_position_at( + addr: u128, block: BlockNumberFor, ) -> Result, DispatchError> { let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); @@ -643,14 +807,335 @@ pub mod pallet { if upoint.bias < 0_i128 { upoint.bias = 0_i128 } - Ok(upoint - .amount - .checked_add( - &T::VoteWeightMultiplier::get() - .checked_mul(&(upoint.bias as u128).unique_saturated_into()) - .ok_or(ArithmeticError::Overflow)?, - ) + Ok(T::VoteWeightMultiplier::get() + .checked_mul((upoint.bias as u128).unique_saturated_into()) .ok_or(ArithmeticError::Overflow)?) } + + pub(crate) fn balance_of_at( + addr: &AccountIdOf, + block: BlockNumberFor, + ) -> Result, DispatchError> { + let mut balance = BalanceOf::::zero(); + UserPositions::::get(addr).into_iter().try_for_each( + |position| -> DispatchResult { + balance = balance + .checked_add(Self::balance_of_position_at(position, block)?) + .ok_or(ArithmeticError::Overflow)?; + Ok(()) + }, + )?; + Ok(balance) + } + + pub(crate) fn balance_of_current_block( + addr: &AccountIdOf, + ) -> Result, DispatchError> { + let mut balance = BalanceOf::::zero(); + UserPositions::::get(addr).into_iter().try_for_each( + |position| -> DispatchResult { + balance = balance + .checked_add(Self::balance_of_position_current_block(position)?) + .ok_or(ArithmeticError::Overflow)?; + Ok(()) + }, + )?; + Ok(balance) + } + + pub fn markup_calc( + addr: &AccountIdOf, + position: u128, + mut old_locked: LockedBalance, BlockNumberFor>, + mut new_locked: LockedBalance, BlockNumberFor>, + user_markup_info: Option<&UserMarkupInfo>, + ) -> DispatchResult { + if let Some(info) = user_markup_info { + old_locked.amount = FixedU128::from_inner(old_locked.amount) + .checked_mul(&info.old_markup_coefficient) + .and_then(|x| x.into_inner().checked_add(old_locked.amount)) + .ok_or(ArithmeticError::Overflow)?; + new_locked.amount = FixedU128::from_inner(new_locked.amount) + .checked_mul(&info.markup_coefficient) + .and_then(|x| x.into_inner().checked_add(new_locked.amount)) + .ok_or(ArithmeticError::Overflow)?; + } + + Self::_checkpoint(addr, position, old_locked.clone(), new_locked.clone())?; + Ok(()) + } + + pub fn deposit_markup_inner( + origin: OriginFor, + asset_id: CurrencyIdOf, + value: BalanceOf, + ) -> DispatchResult { + let addr = ensure_signed(origin)?; + let markup_coefficient = + MarkupCoefficient::::get(asset_id).ok_or(Error::::ArgumentsError)?; // Ensure it is the correct token type. + ensure!(!value.is_zero(), Error::::ArgumentsError); + + TotalLock::::try_mutate(asset_id, |total_lock| -> DispatchResult { + *total_lock = total_lock.checked_add(value).ok_or(ArithmeticError::Overflow)?; + Ok(()) + })?; + + let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); + + let mut user_markup_info = UserMarkupInfos::::get(&addr).unwrap_or_default(); + let delta_coefficient: FixedU128 = markup_coefficient + .markup_coefficient + .checked_mul(&FixedU128::from_inner(value)) + .ok_or(ArithmeticError::Overflow)?; + let mut locked_token = LockedTokens::::get(asset_id, &addr).unwrap_or(LockedToken { + amount: Zero::zero(), + markup_coefficient: Zero::zero(), + refresh_block: current_block_number, + }); + let asset_id_markup_coefficient = + locked_token.markup_coefficient.saturating_add(delta_coefficient); + locked_token.markup_coefficient = + match markup_coefficient.hardcap.cmp(&asset_id_markup_coefficient) { + Ordering::Less => { + Self::update_markup_info( + &addr, + user_markup_info + .markup_coefficient + .saturating_sub(locked_token.markup_coefficient) + .saturating_add(markup_coefficient.hardcap), + &mut user_markup_info, + ); + markup_coefficient.hardcap + }, + Ordering::Equal | Ordering::Greater => { + // TODO: need logic of hardcap + Self::update_markup_info( + &addr, + user_markup_info.markup_coefficient.saturating_add(delta_coefficient), + &mut user_markup_info, + ); + asset_id_markup_coefficient + }, + }; + locked_token.amount = locked_token.amount.saturating_add(value); + locked_token.refresh_block = current_block_number; + + T::MultiCurrency::set_lock(MARKUP_LOCK_ID, asset_id, &addr, locked_token.amount)?; + LockedTokens::::insert(&asset_id, &addr, locked_token); + UserPositions::::get(&addr).into_iter().try_for_each( + |position| -> DispatchResult { + let _locked: LockedBalance, BlockNumberFor> = + Self::locked(position); + ensure!(!_locked.amount.is_zero(), Error::::ArgumentsError); + Self::markup_calc( + &addr, + position, + _locked.clone(), + _locked, + Some(&user_markup_info), + ) + }, + )?; + + // Locked cannot be updated because it is markup, not a lock vBNC + Ok(()) + } + + pub fn withdraw_markup_inner( + origin: OriginFor, + asset_id: CurrencyIdOf, + ) -> DispatchResult { + let addr = ensure_signed(origin)?; + let _ = MarkupCoefficient::::get(asset_id).ok_or(Error::::ArgumentsError)?; // Ensure it is the correct token type. + + let mut user_markup_info = UserMarkupInfos::::get(&addr).unwrap_or_default(); + + let locked_token = + LockedTokens::::get(&asset_id, &addr).ok_or(Error::::LockNotExist)?; + Self::update_markup_info( + &addr, + user_markup_info + .markup_coefficient + .saturating_sub(locked_token.markup_coefficient), + &mut user_markup_info, + ); + TotalLock::::try_mutate(asset_id, |total_lock| -> DispatchResult { + *total_lock = + total_lock.checked_sub(locked_token.amount).ok_or(ArithmeticError::Overflow)?; + Ok(()) + })?; + T::MultiCurrency::remove_lock(MARKUP_LOCK_ID, asset_id, &addr)?; + + LockedTokens::::remove(&asset_id, &addr); + UserPositions::::get(&addr).into_iter().try_for_each( + |position| -> DispatchResult { + let _locked: LockedBalance, BlockNumberFor> = + Self::locked(position); + ensure!(!_locked.amount.is_zero(), Error::::ArgumentsError); // TODO + Self::markup_calc( + &addr, + position, + _locked.clone(), + _locked, + Some(&user_markup_info), + ) + }, + )?; + Ok(()) + } + + pub fn refresh_inner(origin: OriginFor, asset_id: CurrencyIdOf) -> DispatchResult { + let _who = ensure_signed(origin)?; + + let markup_coefficient = + MarkupCoefficient::::get(asset_id).ok_or(Error::::ArgumentsError)?; + let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); + let limit = T::MarkupRefreshLimit::get(); + let mut all_refreshed = true; + let mut refresh_count = 0; + let locked_tokens = LockedTokens::::iter_prefix(&asset_id); + + for (addr, mut locked_token) in locked_tokens { + if refresh_count >= limit { + all_refreshed = false; + break; + } + + if locked_token.refresh_block <= markup_coefficient.update_block { + locked_token.refresh_block = current_block_number; + + let new_markup_coefficient = markup_coefficient + .markup_coefficient + .checked_mul(&FixedU128::from_inner(locked_token.amount)) + .ok_or(ArithmeticError::Overflow)?; + let mut user_markup_info = + UserMarkupInfos::::get(&addr).ok_or(Error::::LockNotExist)?; + + locked_token.markup_coefficient = + match markup_coefficient.hardcap.cmp(&new_markup_coefficient) { + Ordering::Less => { + Self::update_markup_info( + &addr, + user_markup_info + .markup_coefficient + .saturating_sub(locked_token.markup_coefficient) + .saturating_add(markup_coefficient.hardcap), + &mut user_markup_info, + ); + markup_coefficient.hardcap + }, + Ordering::Equal | Ordering::Greater => { + Self::update_markup_info( + &addr, + user_markup_info + .markup_coefficient + .saturating_sub(locked_token.markup_coefficient) + .saturating_add(new_markup_coefficient), + &mut user_markup_info, + ); + new_markup_coefficient + }, + }; + + LockedTokens::::insert(&asset_id, &addr, locked_token); + UserPositions::::get(&addr).into_iter().try_for_each( + |position| -> DispatchResult { + let _locked: LockedBalance, BlockNumberFor> = + Self::locked(position); + ensure!(!_locked.amount.is_zero(), Error::::ArgumentsError); // TODO + Self::markup_calc( + &addr, + position, + _locked.clone(), + _locked, + Some(&user_markup_info), + ) + }, + )?; + + refresh_count += 1; + } + } + + if all_refreshed { + Self::deposit_event(Event::AllRefreshed { asset_id }); + } else { + Self::deposit_event(Event::PartiallyRefreshed { asset_id }); + } + Ok(()) + } + + /// Withdraw vBNC by position + /// + /// # Arguments + /// + /// * `who` - the user of the position + /// * `position` - the ID of the position + /// * `_locked` - user locked variable representation + /// * `if_fast` - distinguish whether it is a fast withdraw + + pub fn withdraw_no_ensure( + who: &AccountIdOf, + position: u128, + mut _locked: LockedBalance, BlockNumberFor>, + if_fast: Option, + ) -> DispatchResult { + let value = _locked.amount; + let old_locked: LockedBalance, BlockNumberFor> = _locked.clone(); + _locked.end = Zero::zero(); + _locked.amount = Zero::zero(); + Locked::::insert(position, _locked.clone()); + + let supply_before = Self::supply(); + Supply::::set(supply_before.saturating_sub(value)); + + // BNC should be transferred before checkpoint + UserPositions::::mutate(who, |positions| { + positions.retain(|&x| x != position); + }); + UserPointEpoch::::remove(position); + let new_locked_balance = + UserLocked::::get(who).checked_sub(value).ok_or(ArithmeticError::Underflow)?; + T::MultiCurrency::set_lock(VE_LOCK_ID, T::TokenType::get(), who, new_locked_balance)?; + UserLocked::::set(who, new_locked_balance); + if let Some(fast) = if_fast { + if fast != Perbill::zero() { + T::MultiCurrency::transfer( + T::TokenType::get(), + who, + &T::VeMintingPalletId::get().into_account_truncating(), + fast * value, + )?; + } + } + + Self::_checkpoint(who, position, old_locked, _locked.clone())?; + + Self::deposit_event(Event::Withdrawn { addr: position, value }); + Self::deposit_event(Event::Supply { + supply_before, + supply: supply_before.saturating_sub(value), + }); + Ok(()) + } + + fn redeem_commission( + remaining_blocks: BlockNumberFor, + ) -> Result { + let commission = FixedU128::from_inner(remaining_blocks.saturated_into::()) + .checked_add(&FixedU128::from_inner(2_628_000)) + .and_then(|x| x.checked_div(&FixedU128::from_inner(10_512_000))) + .and_then(|x| Some(x.saturating_pow(2))) + .ok_or(ArithmeticError::Overflow)?; + Ok(Perbill::from_rational(commission.into_inner(), 1_000_000)) + } + + fn redeem_unlock_inner(who: &AccountIdOf, position: u128) -> DispatchResult { + let mut _locked = Self::locked(position); + let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); + ensure!(_locked.end > current_block_number, Error::::Expired); + let fast = Self::redeem_commission(_locked.end - current_block_number)?; + Self::withdraw_no_ensure(who, position, _locked, Some(fast)) + } } } diff --git a/pallets/ve-minting/src/mock.rs b/pallets/ve-minting/src/mock.rs index 74a4472a76..c4ee84942b 100644 --- a/pallets/ve-minting/src/mock.rs +++ b/pallets/ve-minting/src/mock.rs @@ -152,7 +152,7 @@ impl orml_tokens::Config for Runtime { type DustRemovalWhitelist = Nothing; type RuntimeEvent = RuntimeEvent; type ExistentialDeposits = ExistentialDeposits; - type MaxLocks = (); + type MaxLocks = ConstU32<50>; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); @@ -194,6 +194,7 @@ parameter_types! { pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); } ord_parameter_types! { @@ -210,6 +211,7 @@ impl bifrost_vtoken_minting::Config for Runtime { type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type BifrostSlp = Slp; type BifrostSlpx = SlpxInterface; type RelayChainToken = RelayCurrencyId; @@ -224,6 +226,9 @@ impl bifrost_vtoken_minting::Config for Runtime { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } ord_parameter_types! { @@ -243,7 +248,9 @@ parameter_types! { pub const Week: BlockNumber = 50400; // a week pub const MaxBlock: BlockNumber = 10512000; // four years pub const Multiplier: Balance = 10_u128.pow(12); - pub const VoteWeightMultiplier: Balance = 3; + pub const VoteWeightMultiplier: Balance = 1; + pub const MaxPositions: u32 = 10; + pub const MarkupRefreshLimit: u32 = 100; } impl bifrost_ve_minting::Config for Runtime { @@ -259,6 +266,8 @@ impl bifrost_ve_minting::Config for Runtime { type MaxBlock = MaxBlock; type Multiplier = Multiplier; type VoteWeightMultiplier = VoteWeightMultiplier; + type MaxPositions = MaxPositions; + type MarkupRefreshLimit = MarkupRefreshLimit; } pub struct SubAccountIndexMultiLocationConvertor; diff --git a/pallets/ve-minting/src/tests.rs b/pallets/ve-minting/src/tests.rs index bbeacecf48..a321a83acb 100644 --- a/pallets/ve-minting/src/tests.rs +++ b/pallets/ve-minting/src/tests.rs @@ -26,21 +26,22 @@ use bifrost_primitives::TokenInfo; use bifrost_runtime_common::milli; use frame_support::{assert_noop, assert_ok}; +const POSITIONID0: u128 = 0; +const POOLID0: PoolId = 0; + #[test] -fn _checkpoint() { +fn create_lock() { ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { asset_registry(); System::set_block_number(System::block_number() + 20); - let old_locked = LockedBalance { amount: 0, end: 0 }; - let new_locked = LockedBalance { - amount: 10000000000000, - end: System::block_number() + 365 * 86400 / 12, - }; assert_ok!(VeMinting::set_config(RuntimeOrigin::root(), Some(0), Some(7 * 86400 / 12))); - System::set_block_number(System::block_number() + 20); - assert_ok!(VeMinting::_checkpoint(&BOB, old_locked, new_locked)); - assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(7499936934420)); + assert_ok!(VeMinting::create_lock_inner( + &BOB, + 10_000_000_000_000, + System::block_number() + 4 * 365 * 86400 / 12, + )); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(9972575751740)); }); } @@ -57,12 +58,13 @@ fn update_reward() { 100_000_000_000, System::block_number() + 365 * 86400 / 12, )); - assert_eq!(VeMinting::balance_of(&BOB, None), Ok(174785436640)); - assert_ok!(VeMinting::deposit_for(&BOB, 100_000_000_000)); - assert_ok!(VeMinting::update_reward(Some(&BOB))); + assert_eq!(VeMinting::balance_of(&BOB, None), Ok(24928478880)); + assert_eq!(VeMinting::balance_of_position_current_block(0), Ok(24928478880)); + assert_ok!(VeMinting::deposit_for(&BOB, 0, 100_000_000_000)); + assert_ok!(VeMinting::update_reward(POOLID0, Some(&BOB), None)); // TODO - assert_eq!(VeMinting::balance_of(&BOB, None), Ok(349578735500)); - assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(349578735500)); + assert_eq!(VeMinting::balance_of(&BOB, None), Ok(49859578500)); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(49859578500)); }); } @@ -91,22 +93,20 @@ fn notify_reward_amount() { assert_ok!(VeMinting::set_config(RuntimeOrigin::root(), Some(0), Some(7 * 86400 / 12))); System::set_block_number(System::block_number() + 40); - assert_noop!( - VeMinting::get_rewards(RuntimeOrigin::signed(BOB)), - Error::::NoRewards - ); + assert_ok!(VeMinting::get_rewards(RuntimeOrigin::signed(BOB))); // balance of veBNC is 0 assert_ok!(VeMinting::create_lock_inner( &BOB, 20_000_000_000, System::block_number() + 4 * 365 * 86400 / 12 )); assert_eq!(Tokens::free_balance(KSM, &BOB), 0); + // balance of veBNC is not 0 assert_noop!( VeMinting::get_rewards(RuntimeOrigin::signed(BOB)), Error::::NoRewards ); - assert_ok!(VeMinting::increase_amount(RuntimeOrigin::signed(BOB), 80_000_000_000)); - assert_eq!(VeMinting::balance_of(&BOB, None), Ok(399146883040)); + assert_ok!(VeMinting::increase_amount(RuntimeOrigin::signed(BOB), 0, 80_000_000_000)); + assert_eq!(VeMinting::balance_of(&BOB, None), Ok(99715627680)); let rewards = vec![(KSM, 1_000_000_000)]; assert_ok!(VeMinting::notify_rewards( @@ -121,7 +121,7 @@ fn notify_reward_amount() { assert_ok!(VeMinting::get_rewards(RuntimeOrigin::signed(BOB))); assert_eq!(Tokens::free_balance(KSM, &BOB), 396819); System::set_block_number(System::block_number() + 7 * 86400 / 12); - assert_ok!(VeMinting::get_rewards_inner(&BOB)); + assert_ok!(VeMinting::get_rewards_inner(POOLID0, &BOB, None)); assert_eq!(Tokens::free_balance(KSM, &BOB), 999986398); assert_ok!(VeMinting::notify_rewards( RuntimeOrigin::root(), @@ -129,21 +129,17 @@ fn notify_reward_amount() { Some(7 * 86400 / 12), rewards )); - assert_ok!(VeMinting::create_lock_inner( - &CHARLIE, - 100_000_000_000, - System::block_number() + 4 * 365 * 86400 / 12 - )); + assert_ok!(VeMinting::create_lock_inner(&CHARLIE, 100_000_000_000, 4 * 365 * 86400 / 12)); System::set_block_number(System::block_number() + 1 * 86400 / 12); - assert_ok!(VeMinting::get_rewards_inner(&BOB)); - assert_eq!(Tokens::free_balance(KSM, &BOB), 1071285014); - assert_ok!(VeMinting::get_rewards_inner(&CHARLIE)); - assert_eq!(Tokens::free_balance(KSM, &CHARLIE), 71556583); + assert_ok!(VeMinting::get_rewards_inner(POOLID0, &BOB, None)); + assert_eq!(Tokens::free_balance(KSM, &BOB), 1071241763); + assert_ok!(VeMinting::get_rewards_inner(POOLID0, &CHARLIE, None)); + assert_eq!(Tokens::free_balance(KSM, &CHARLIE), 71599834); System::set_block_number(System::block_number() + 7 * 86400 / 12); - assert_ok!(VeMinting::get_rewards_inner(&CHARLIE)); - assert_eq!(Tokens::free_balance(KSM, &CHARLIE), 500898890); - assert_ok!(VeMinting::get_rewards_inner(&BOB)); - assert_eq!(Tokens::free_balance(KSM, &BOB), 1499073906); + assert_ok!(VeMinting::get_rewards_inner(POOLID0, &CHARLIE, None)); + assert_eq!(Tokens::free_balance(KSM, &CHARLIE), 501203849); + assert_ok!(VeMinting::get_rewards_inner(POOLID0, &BOB, None)); + assert_eq!(Tokens::free_balance(KSM, &BOB), 1498768947); }); } @@ -177,12 +173,13 @@ fn create_lock_to_withdraw() { VeMinting::total_supply(System::block_number()) ); assert_noop!( - VeMinting::increase_amount(RuntimeOrigin::signed(BOB), 50_000_000_000_000), + VeMinting::increase_amount(RuntimeOrigin::signed(BOB), POSITIONID0, 50_000_000_000_000), Error::::LockNotExist ); assert_noop!( VeMinting::increase_unlock_time( RuntimeOrigin::signed(BOB), + POSITIONID0, System::block_number() + 365 * 86400 / 12 ), Error::::LockNotExist @@ -199,64 +196,55 @@ fn create_lock_to_withdraw() { VeMinting::create_lock( RuntimeOrigin::signed(BOB), 50_000_000_000_000, - System::block_number() + 7 * 86400 / 12 - 1 + 7 * 86400 / 12 - 1 ), Error::::Expired ); assert_noop!( - VeMinting::create_lock( - RuntimeOrigin::signed(BOB), - 50_000, - System::block_number() + 7 * 86400 / 12 - ), + VeMinting::create_lock(RuntimeOrigin::signed(BOB), 50_000, 7 * 86400 / 12), Error::::BelowMinimumMint ); - assert_ok!(VeMinting::create_lock_inner( - &BOB, - 50_000_000_000_000, - System::block_number() + 365 * 86400 / 12 - )); - assert_noop!( - VeMinting::create_lock_inner( - &BOB, - 50_000_000_000_000, - System::block_number() + 365 * 86400 / 12 - ), - Error::::LockExist - ); + assert_ok!(VeMinting::create_lock_inner(&BOB, 50_000_000_000_000, 365 * 86400 / 12)); assert_noop!( VeMinting::increase_unlock_time( RuntimeOrigin::signed(BOB), + POSITIONID0, System::block_number() + 5 * 365 * 86400 / 12 ), Error::::Expired ); - assert_eq!(VeMinting::balance_of_at(&BOB, System::block_number()), Ok(87397254003200)); + assert_eq!(VeMinting::balance_of_at(&BOB, System::block_number()), Ok(12465751334400)); assert_eq!(VeMinting::balance_of_at(&BOB, System::block_number() - 10), Ok(0)); assert_eq!(VeMinting::balance_of_at(&BOB, 0), Ok(0)); assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number() - 10)), Ok(0)); - assert_eq!(VeMinting::total_supply(System::block_number()), Ok(87397254003200)); + assert_eq!(VeMinting::total_supply(System::block_number()), Ok(12465751334400)); assert_noop!( - VeMinting::increase_amount(RuntimeOrigin::signed(BOB), 50_000), + VeMinting::increase_amount(RuntimeOrigin::signed(BOB), 0, 50_000), Error::::BelowMinimumMint ); - assert_ok!(VeMinting::increase_amount(RuntimeOrigin::signed(BOB), 50_000_000_000_000)); + assert_ok!(VeMinting::increase_amount(RuntimeOrigin::signed(BOB), 0, 50_000_000_000_000)); log::debug!( "3System::block_number():{:?} total_supply:{:?}", System::block_number(), VeMinting::total_supply(System::block_number()) ); - assert_eq!(VeMinting::balance_of(&BOB, None), Ok(174794515868800)); - assert_eq!(VeMinting::total_supply(System::block_number()), Ok(174794515868800)); + assert_eq!(VeMinting::balance_of(&BOB, None), Ok(24931505289600)); + assert_eq!(VeMinting::total_supply(System::block_number()), Ok(24931505289600)); - assert_ok!(VeMinting::withdraw(RuntimeOrigin::signed(ALICE))); - assert_noop!(VeMinting::withdraw(RuntimeOrigin::signed(BOB)), Error::::Expired); - assert_eq!(VeMinting::total_supply(System::block_number()), Ok(174794515868800)); + assert_noop!( + VeMinting::withdraw(RuntimeOrigin::signed(ALICE), POSITIONID0), + Error::::LockNotExist + ); + assert_noop!( + VeMinting::withdraw(RuntimeOrigin::signed(BOB), POSITIONID0), + Error::::Expired + ); + assert_eq!(VeMinting::total_supply(System::block_number()), Ok(24931505289600)); System::set_block_number(System::block_number() + 2 * 365 * 86400 / 12); - assert_eq!(VeMinting::balance_of(&BOB, None), Ok(100_000_000_000_000)); - assert_eq!(VeMinting::total_supply(System::block_number()), Ok(100_000_000_000_000)); - assert_ok!(VeMinting::withdraw(RuntimeOrigin::signed(BOB))); - assert_ok!(VeMinting::withdraw_inner(&BOB)); + assert_eq!(VeMinting::balance_of(&BOB, None), Ok(0)); + assert_eq!(VeMinting::total_supply(System::block_number()), Ok(0)); + assert_ok!(VeMinting::withdraw(RuntimeOrigin::signed(BOB), POSITIONID0)); + assert_ok!(VeMinting::withdraw_inner(&BOB, 0)); log::debug!( "5System::block_number():{:?} total_supply:{:?}", System::block_number(), @@ -273,7 +261,173 @@ fn overflow() { asset_registry(); assert_ok!(VeMinting::create_lock_inner(&BOB, 100_000_000_000_000, 77000)); System::set_block_number(77001); - assert_eq!(VeMinting::balance_of(&BOB, Some(77001)), Ok(100_000_000_000_000)); - assert_eq!(VeMinting::total_supply(System::block_number()), Ok(100_000_000_000_000)); + assert_eq!(VeMinting::balance_of(&BOB, Some(77001)), Ok(0)); + assert_eq!(VeMinting::total_supply(System::block_number()), Ok(0)); + }); +} + +#[test] +fn deposit_markup_before_lock_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + System::set_block_number(System::block_number() + 20); + + assert_ok!(VeMinting::set_config(RuntimeOrigin::root(), Some(0), Some(7 * 86400 / 12))); + assert_ok!(VeMinting::set_markup_coefficient( + RuntimeOrigin::root(), + VBNC, + 1_000.into(), + 10_000_000_000_000.into() + )); + assert_ok!(VeMinting::deposit_markup(RuntimeOrigin::signed(BOB), VBNC, 10_000_000_000_000)); + assert_ok!(VeMinting::create_lock_inner( + &BOB, + 10_000_000_000_000, + System::block_number() + 365 * 86400 / 12, + )); + assert_eq!( + UserPointHistory::::get(POSITIONID0, U256::one()), + Point { bias: 2518061148680, slope: 960806, block: 20, amount: 10100000000000 } + ); + assert_eq!(Locked::::get(POSITIONID0).amount, 10_000_000_000_000); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(2518061148680)); + }); +} + +#[test] +fn deposit_markup_after_lock_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + System::set_block_number(System::block_number() + 20); + + assert_ok!(VeMinting::set_config(RuntimeOrigin::root(), Some(0), Some(7 * 86400 / 12))); + assert_ok!(VeMinting::create_lock_inner( + &BOB, + 10_000_000_000_000, + System::block_number() + 365 * 86400 / 12, + )); + assert_ok!(VeMinting::set_markup_coefficient( + RuntimeOrigin::root(), + VBNC, + 1_000.into(), + 10_000_000_000_000.into() + )); + assert_eq!( + UserPointHistory::::get(POSITIONID0, U256::from(1)), + Point { bias: 2493129668540, slope: 951293, block: 20, amount: 10000000000000 } + ); + assert_ok!(VeMinting::deposit_markup(RuntimeOrigin::signed(BOB), VBNC, 10_000_000_000_000)); + assert_eq!( + UserPointHistory::::get(POSITIONID0, U256::from(2)), + Point { bias: 2518061148680, slope: 960806, block: 20, amount: 10100000000000 } + ); + assert_eq!(Locked::::get(POSITIONID0).amount, 10_000_000_000_000); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(2518061148680)); + }); +} + +#[test] +fn withdraw_markup_after_lock_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + System::set_block_number(System::block_number() + 20); + + assert_ok!(VeMinting::set_config(RuntimeOrigin::root(), Some(0), Some(7 * 86400 / 12))); + assert_ok!(VeMinting::create_lock_inner( + &BOB, + 10_000_000_000_000, + System::block_number() + 365 * 86400 / 12, + )); + assert_ok!(VeMinting::set_markup_coefficient( + RuntimeOrigin::root(), + VBNC, + 1_000.into(), + 10_000_000_000_000.into() + )); + assert_ok!(VeMinting::deposit_markup(RuntimeOrigin::signed(BOB), VBNC, 10_000_000_000_000)); + assert_ok!(VeMinting::withdraw_markup(RuntimeOrigin::signed(BOB), VBNC)); + assert_eq!( + UserPointHistory::::get(POSITIONID0, U256::from(2)), + Point { bias: 2518061148680, slope: 960806, block: 20, amount: 10100000000000 } + ); + assert_eq!( + UserPointHistory::::get(POSITIONID0, U256::from(3)), + Point { bias: 2493129668540, slope: 951293, block: 20, amount: 10000000000000 } + ); + assert_eq!(Locked::::get(POSITIONID0).amount, 10_000_000_000_000); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(2493129668540)); + }); +} + +#[test] +fn redeem_unlock_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + System::set_block_number(System::block_number() + 20); + + assert_ok!(VeMinting::set_config(RuntimeOrigin::root(), Some(0), Some(7 * 86400 / 12))); + assert_ok!(VeMinting::set_markup_coefficient( + RuntimeOrigin::root(), + VBNC, + 1_000.into(), + 10_000_000_000_000.into() + )); + assert_ok!(VeMinting::deposit_markup(RuntimeOrigin::signed(BOB), VBNC, 10_000_000_000_000)); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(0)); + assert_ok!(VeMinting::create_lock_inner( + &BOB, + 10_000_000_000_000, + System::block_number() + 365 * 86400 / 12, + )); + assert_eq!( + UserPointHistory::::get(POSITIONID0, U256::one()), + Point { bias: 2518061148680, slope: 960806, block: 20, amount: 10100000000000 } + ); + assert_eq!(Locked::::get(POSITIONID0).amount, 10_000_000_000_000); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(2518061148680)); + assert_ok!(VeMinting::redeem_unlock(RuntimeOrigin::signed(BOB), 0)); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(0)); + }); +} + +#[test] +fn refresh_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + asset_registry(); + System::set_block_number(System::block_number() + 20); + + assert_ok!(VeMinting::set_config(RuntimeOrigin::root(), Some(0), Some(7 * 86400 / 12))); + assert_ok!(VeMinting::set_markup_coefficient( + RuntimeOrigin::root(), + VBNC, + 1_000.into(), + 10_000_000_000_000.into() + )); + assert_ok!(VeMinting::deposit_markup(RuntimeOrigin::signed(BOB), VBNC, 10_000_000_000_000)); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(0)); + assert_ok!(VeMinting::create_lock_inner( + &BOB, + 10_000_000_000_000, + System::block_number() + 365 * 86400 / 12, + )); + assert_ok!(VeMinting::set_markup_coefficient( + RuntimeOrigin::root(), + VBNC, + 2_000.into(), + 10_000_000_000_000.into() + )); + assert_ok!(VeMinting::refresh_inner(RuntimeOrigin::signed(BOB), VBNC)); + assert_eq!( + UserPointHistory::::get(POSITIONID0, U256::one()), + Point { bias: 2518061148680, slope: 960806, block: 20, amount: 10100000000000 } + ); + assert_eq!( + UserPointHistory::::get(POSITIONID0, U256::from(2)), + Point { bias: 2542992628820, slope: 970319, block: 20, amount: 10200000000000 } + ); + assert_eq!(Locked::::get(POSITIONID0).amount, 10_000_000_000_000); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(2542992628820)); + assert_ok!(VeMinting::redeem_unlock(RuntimeOrigin::signed(BOB), 0)); + assert_eq!(VeMinting::balance_of(&BOB, Some(System::block_number())), Ok(0)); }); } diff --git a/pallets/ve-minting/src/traits.rs b/pallets/ve-minting/src/traits.rs index 3038a437f6..a6adcdef7b 100644 --- a/pallets/ve-minting/src/traits.rs +++ b/pallets/ve-minting/src/traits.rs @@ -16,12 +16,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use bifrost_primitives::PoolId; + // Ensure we're `no_std` when compiling for Wasm. use crate::*; pub trait VeMintingInterface { - fn deposit_for(addr: &AccountId, value: Balance) -> DispatchResult; - fn withdraw_inner(addr: &AccountId) -> DispatchResult; + fn deposit_for(_who: &AccountId, position: u128, value: Balance) -> DispatchResult; + fn withdraw_inner(who: &AccountId, position: u128) -> DispatchResult; fn balance_of(addr: &AccountId, time: Option) -> Result; fn total_supply(t: BlockNumber) -> Result; fn supply_at( @@ -30,33 +32,72 @@ pub trait VeMintingInterface { ) -> Result; fn find_block_epoch(_block: BlockNumber, max_epoch: U256) -> U256; fn create_lock_inner( - addr: &AccountId, + who: &AccountId, _value: Balance, _unlock_time: BlockNumber, ) -> DispatchResult; // Deposit `_value` BNC for `addr` and lock until `_unlock_time` - fn increase_amount_inner(addr: &AccountId, value: Balance) -> DispatchResult; // Deposit `_value` additional BNC for `addr` without modifying the unlock time - fn increase_unlock_time_inner(addr: &AccountId, _unlock_time: BlockNumber) -> DispatchResult; // Extend the unlock time for `addr` to `_unlock_time` + fn increase_amount_inner(who: &AccountId, position: u128, value: Balance) -> DispatchResult; // Deposit `_value` additional BNC for `addr` without modifying the unlock time + fn increase_unlock_time_inner( + who: &AccountId, + position: u128, + _unlock_time: BlockNumber, + ) -> DispatchResult; // Extend the unlock time for `addr` to `_unlock_time` + fn auto_notify_reward( + pool_id: PoolId, + n: BlockNumber, + rewards: Vec<(CurrencyId, Balance)>, + ) -> DispatchResult; + fn update_reward( + pool_id: PoolId, + addr: Option<&AccountId>, + share_info: Option<(Balance, Balance)>, + ) -> DispatchResult; + fn get_rewards( + pool_id: PoolId, + addr: &AccountId, + share_info: Option<(Balance, Balance)>, + ) -> DispatchResult; + fn set_incentive( + pool_id: PoolId, + rewards_duration: Option, + controller: Option, + ); + fn add_reward( + addr: &AccountId, + conf: &mut IncentiveConfig, + rewards: &Vec<(CurrencyId, Balance)>, + remaining: Balance, + ) -> DispatchResult; } impl VeMintingInterface, CurrencyIdOf, BalanceOf, BlockNumberFor> for Pallet { fn create_lock_inner( - addr: &AccountIdOf, + who: &AccountIdOf, _value: BalanceOf, _unlock_time: BlockNumberFor, ) -> DispatchResult { + let new_position = Position::::get(); + let mut user_positions = UserPositions::::get(who); + user_positions + .try_push(new_position) + .map_err(|_| Error::::ExceedsMaxPositions)?; + UserPositions::::insert(who, user_positions); + Position::::set(new_position + 1); + let ve_config = Self::ve_configs(); ensure!(_value >= ve_config.min_mint, Error::::BelowMinimumMint); - let _locked: LockedBalance, BlockNumberFor> = Self::locked(addr); + let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); + let _locked: LockedBalance, BlockNumberFor> = Self::locked(new_position); let unlock_time: BlockNumberFor = _unlock_time + .saturating_add(current_block_number) .checked_div(&T::Week::get()) .ok_or(ArithmeticError::Overflow)? .checked_mul(&T::Week::get()) .ok_or(ArithmeticError::Overflow)?; - let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); ensure!( unlock_time >= ve_config.min_block.saturating_add(current_block_number), Error::::Expired @@ -67,9 +108,9 @@ impl VeMintingInterface, CurrencyIdOf, BalanceOf ); ensure!(_locked.amount == BalanceOf::::zero(), Error::::LockExist); // Withdraw old tokens first - Self::_deposit_for(addr, _value, unlock_time, _locked)?; + Self::_deposit_for(who, new_position, _value, unlock_time, _locked)?; Self::deposit_event(Event::LockCreated { - addr: addr.to_owned(), + addr: who.to_owned(), value: _value, unlock_time: _unlock_time, }); @@ -77,11 +118,12 @@ impl VeMintingInterface, CurrencyIdOf, BalanceOf } fn increase_unlock_time_inner( - addr: &AccountIdOf, + who: &AccountIdOf, + position: u128, _unlock_time: BlockNumberFor, ) -> DispatchResult { let ve_config = Self::ve_configs(); - let _locked: LockedBalance, BlockNumberFor> = Self::locked(addr); + let _locked: LockedBalance, BlockNumberFor> = Self::locked(position); let unlock_time: BlockNumberFor = _unlock_time .checked_div(&T::Week::get()) .ok_or(ArithmeticError::Overflow)? @@ -100,60 +142,40 @@ impl VeMintingInterface, CurrencyIdOf, BalanceOf ensure!(_locked.amount > BalanceOf::::zero(), Error::::LockNotExist); ensure!(_locked.end > current_block_number, Error::::Expired); // Cannot add to expired/non-existent lock - Self::_deposit_for(addr, BalanceOf::::zero(), unlock_time, _locked)?; + Self::_deposit_for(who, position, BalanceOf::::zero(), unlock_time, _locked)?; Self::deposit_event(Event::UnlockTimeIncreased { - addr: addr.to_owned(), + addr: position.to_owned(), unlock_time: _unlock_time, }); Ok(()) } - fn increase_amount_inner(addr: &AccountIdOf, value: BalanceOf) -> DispatchResult { + fn increase_amount_inner( + who: &AccountIdOf, + position: u128, + value: BalanceOf, + ) -> DispatchResult { let ve_config = Self::ve_configs(); ensure!(value >= ve_config.min_mint, Error::::BelowMinimumMint); - let _locked: LockedBalance, BlockNumberFor> = Self::locked(addr); - ensure!(_locked.amount > Zero::zero(), Error::::LockNotExist); // Need to be executed after create_lock + let _locked: LockedBalance, BlockNumberFor> = Self::locked(position); + ensure!(_locked.amount > BalanceOf::::zero(), Error::::LockNotExist); // Need to be executed after create_lock let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); ensure!(_locked.end > current_block_number, Error::::Expired); // Cannot add to expired/non-existent lock - Self::_deposit_for(addr, value, Zero::zero(), _locked)?; - Self::deposit_event(Event::AmountIncreased { addr: addr.to_owned(), value }); + Self::_deposit_for(who, position, value, Zero::zero(), _locked)?; + Self::deposit_event(Event::AmountIncreased { who: who.to_owned(), position, value }); Ok(()) } - fn deposit_for(addr: &AccountIdOf, value: BalanceOf) -> DispatchResult { - let _locked: LockedBalance, BlockNumberFor> = Self::locked(addr); - Self::_deposit_for(addr, value, Zero::zero(), _locked) + fn deposit_for(who: &AccountIdOf, position: u128, value: BalanceOf) -> DispatchResult { + let _locked: LockedBalance, BlockNumberFor> = Self::locked(position); + Self::_deposit_for(who, position, value, Zero::zero(), _locked) } - fn withdraw_inner(addr: &AccountIdOf) -> DispatchResult { - let mut _locked = Self::locked(addr); + fn withdraw_inner(who: &AccountIdOf, position: u128) -> DispatchResult { + let mut _locked = Self::locked(position); let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); ensure!(current_block_number >= _locked.end, Error::::Expired); - let value = _locked.amount; - let old_locked: LockedBalance, BlockNumberFor> = _locked.clone(); - _locked.end = Zero::zero(); - _locked.amount = Zero::zero(); - Locked::::insert(addr, _locked.clone()); - - let supply_before = Self::supply(); - Supply::::set(supply_before.saturating_sub(value)); - - // BNC should be transferred before checkpoint - T::MultiCurrency::transfer( - T::TokenType::get(), - &T::VeMintingPalletId::get().into_account_truncating(), - addr, - value, - )?; - - Self::_checkpoint(addr, old_locked, _locked.clone())?; - - Self::deposit_event(Event::Withdrawn { addr: addr.to_owned(), value }); - Self::deposit_event(Event::Supply { - supply_before, - supply: supply_before.saturating_sub(value), - }); - Ok(()) + Self::withdraw_no_ensure(who, position, _locked, None) } fn balance_of( @@ -235,47 +257,69 @@ impl VeMintingInterface, CurrencyIdOf, BalanceOf if last_point.bias < 0_i128 { last_point.bias = 0_i128 } - Ok(last_point - .amount - .checked_add( - &T::VoteWeightMultiplier::get() - .checked_mul(&(last_point.bias as u128).unique_saturated_into()) - .ok_or(ArithmeticError::Overflow)?, - ) + Ok(T::VoteWeightMultiplier::get() + .checked_mul((last_point.bias as u128).unique_saturated_into()) .ok_or(ArithmeticError::Overflow)?) } -} -pub trait Incentive { - fn set_incentive(rewards_duration: Option); - fn add_reward( - addr: &AccountId, - conf: &mut IncentiveConfig, - rewards: &Vec<(CurrencyId, Balance)>, - remaining: Balance, - ) -> DispatchResult; -} + fn auto_notify_reward( + pool_id: PoolId, + n: BlockNumberFor, + rewards: Vec<(CurrencyIdOf, BalanceOf)>, + ) -> DispatchResult { + let conf = Self::incentive_configs(pool_id); + if n == conf.last_update_time + conf.rewards_duration { + Self::notify_reward_amount(pool_id, &conf.incentive_controller, rewards)?; + } + Ok(()) + } + + fn update_reward( + pool_id: PoolId, + addr: Option<&AccountIdOf>, + share_info: Option<(BalanceOf, BalanceOf)>, + ) -> DispatchResult { + Self::update_reward(pool_id, addr, share_info) + } + + fn get_rewards( + pool_id: PoolId, + addr: &AccountIdOf, + share_info: Option<(BalanceOf, BalanceOf)>, + ) -> DispatchResult { + Self::get_rewards_inner(pool_id, addr, share_info) + } + + fn set_incentive( + pool_id: PoolId, + rewards_duration: Option>, + controller: Option>, + ) { + let mut incentive_config = Self::incentive_configs(pool_id); -impl Incentive, CurrencyIdOf, BalanceOf, BlockNumberFor> - for Pallet -{ - fn set_incentive(rewards_duration: Option>) { if let Some(rewards_duration) = rewards_duration { - let mut incentive_config = Self::incentive_configs(); incentive_config.rewards_duration = rewards_duration; - IncentiveConfigs::::set(incentive_config); - Self::deposit_event(Event::IncentiveSet { rewards_duration }); }; + if let Some(controller) = controller { + incentive_config.incentive_controller = Some(controller.clone()); + } + IncentiveConfigs::::set(pool_id, incentive_config.clone()); + Self::deposit_event(Event::IncentiveSet { incentive_config }); } fn add_reward( addr: &AccountIdOf, - conf: &mut IncentiveConfig, BalanceOf, BlockNumberFor>, + conf: &mut IncentiveConfig< + CurrencyIdOf, + BalanceOf, + BlockNumberFor, + AccountIdOf, + >, rewards: &Vec<(CurrencyIdOf, BalanceOf)>, remaining: BalanceOf, ) -> DispatchResult { rewards.iter().try_for_each(|(currency, reward)| -> DispatchResult { let mut total_reward: BalanceOf = *reward; - if remaining != Zero::zero() { + if remaining != BalanceOf::::zero() { let leftover: BalanceOf = conf .reward_rate .get(currency) @@ -295,7 +339,7 @@ impl Incentive, CurrencyIdOf, BalanceOf, BlockNu Error::::NotEnoughBalance ); let new_reward = total_reward - .checked_div(&T::BlockNumberToBalance::convert(conf.rewards_duration)) + .checked_div(T::BlockNumberToBalance::convert(conf.rewards_duration)) .ok_or(ArithmeticError::Overflow)?; conf.reward_rate .entry(*currency) @@ -319,26 +363,30 @@ where Balance: orml_traits::arithmetic::Zero, { fn create_lock_inner( - _addr: &AccountId, + _who: &AccountId, _value: Balance, _unlock_time: BlockNumber, ) -> DispatchResult { Ok(()) } - fn increase_unlock_time_inner(_addr: &AccountId, _unlock_time: BlockNumber) -> DispatchResult { + fn increase_unlock_time_inner( + _who: &AccountId, + _position: u128, + _unlock_time: BlockNumber, + ) -> DispatchResult { Ok(()) } - fn increase_amount_inner(_addr: &AccountId, _value: Balance) -> DispatchResult { + fn increase_amount_inner(_who: &AccountId, _position: u128, _value: Balance) -> DispatchResult { Ok(()) } - fn deposit_for(_addr: &AccountId, _value: Balance) -> DispatchResult { + fn deposit_for(_who: &AccountId, _position: u128, _value: Balance) -> DispatchResult { Ok(()) } - fn withdraw_inner(_addr: &AccountId) -> DispatchResult { + fn withdraw_inner(_who: &AccountId, _position: u128) -> DispatchResult { Ok(()) } @@ -360,4 +408,85 @@ where ) -> Result { Ok(Zero::zero()) } + + fn auto_notify_reward( + _pool_id: PoolId, + _n: BlockNumber, + _rewards: Vec<(CurrencyId, Balance)>, + ) -> DispatchResult { + Ok(()) + } + + fn update_reward( + _pool_id: PoolId, + _addr: Option<&AccountId>, + _share_info: Option<(Balance, Balance)>, + ) -> DispatchResult { + Ok(()) + } + + fn get_rewards( + _pool_id: PoolId, + _addr: &AccountId, + _share_info: Option<(Balance, Balance)>, + ) -> DispatchResult { + Ok(()) + } + + fn set_incentive( + _pool_id: PoolId, + _rewards_duration: Option, + _controller: Option, + ) { + } + fn add_reward( + _addr: &AccountId, + _conf: &mut IncentiveConfig, + _rewards: &Vec<(CurrencyId, Balance)>, + _remaining: Balance, + ) -> DispatchResult { + Ok(()) + } +} + +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Default)] +pub struct UserMarkupInfo { + // pub old_locked: LockedBalance, + pub old_markup_coefficient: FixedU128, + pub markup_coefficient: FixedU128, +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct LockedToken { + // pub asset_id: CurrencyId, + pub amount: Balance, + pub markup_coefficient: FixedU128, + pub refresh_block: BlockNumber, +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct MarkupCoefficientInfo { + pub markup_coefficient: FixedU128, + pub hardcap: FixedU128, + pub update_block: BlockNumber, +} + +pub trait MarkupInfo { + fn update_markup_info( + addr: &AccountId, + new_markup_coefficient: FixedU128, + user_markup_info: &mut UserMarkupInfo, + ); +} + +impl MarkupInfo> for Pallet { + fn update_markup_info( + addr: &AccountIdOf, + new_markup_coefficient: FixedU128, + user_markup_info: &mut UserMarkupInfo, + ) { + user_markup_info.old_markup_coefficient = user_markup_info.markup_coefficient; + user_markup_info.markup_coefficient = new_markup_coefficient; + UserMarkupInfos::::insert(addr, user_markup_info); + } } diff --git a/pallets/ve-minting/src/weights.rs b/pallets/ve-minting/src/weights.rs index 752e89a712..9181daf02f 100644 --- a/pallets/ve-minting/src/weights.rs +++ b/pallets/ve-minting/src/weights.rs @@ -60,6 +60,11 @@ pub trait WeightInfo { fn withdraw() -> Weight; fn get_rewards() -> Weight; fn notify_rewards() -> Weight; + fn set_markup_coefficient() -> Weight; + fn deposit_markup() -> Weight; + fn withdraw_markup() -> Weight; + fn redeem_unlock() -> Weight; + fn refresh() -> Weight; } // For backwards compatibility and tests @@ -320,5 +325,217 @@ impl WeightInfo for () { Weight::from_parts(179_695_000, 6196) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `VeMinting::TotalLock` (r:1 w:1) + /// Proof: `VeMinting::TotalLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VeMinting::MarkupCoefficient` (r:0 w:1) + /// Proof: `VeMinting::MarkupCoefficient` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_markup_coefficient() -> Weight { + // Proof Size summary in bytes: + // Measured: `213` + // Estimated: `3678` + // Minimum execution time: 12_353_000 picoseconds. + Weight::from_parts(12_533_000, 0) + .saturating_add(Weight::from_parts(0, 3678)) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(2)) + } + /// Storage: `VeMinting::MarkupCoefficient` (r:1 w:0) + /// Proof: `VeMinting::MarkupCoefficient` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::TotalLock` (r:1 w:1) + /// Proof: `VeMinting::TotalLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VeMinting::UserMarkupInfos` (r:1 w:1) + /// Proof: `VeMinting::UserMarkupInfos` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::LockedTokens` (r:1 w:1) + /// Proof: `VeMinting::LockedTokens` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tokens::Locks` (r:1 w:1) + /// Proof: `Tokens::Locks` (`max_values`: None, `max_size`: Some(1271), added: 3746, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:1 w:1) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(118), added: 2593, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::CurrencyMetadatas` (r:1 w:0) + /// Proof: `AssetRegistry::CurrencyMetadatas` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPositions` (r:1 w:0) + /// Proof: `VeMinting::UserPositions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Locked` (r:1 w:0) + /// Proof: `VeMinting::Locked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::IncentiveConfigs` (r:1 w:1) + /// Proof: `VeMinting::IncentiveConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Epoch` (r:1 w:1) + /// Proof: `VeMinting::Epoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::PointHistory` (r:1 w:105) + /// Proof: `VeMinting::PointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::SlopeChanges` (r:104 w:0) + /// Proof: `VeMinting::SlopeChanges` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointEpoch` (r:1 w:1) + /// Proof: `VeMinting::UserPointEpoch` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointHistory` (r:1 w:1) + /// Proof: `VeMinting::UserPointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Rewards` (r:1 w:1) + /// Proof: `VeMinting::Rewards` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserRewardPerTokenPaid` (r:1 w:1) + /// Proof: `VeMinting::UserRewardPerTokenPaid` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Supply` (r:1 w:0) + /// Proof: `VeMinting::Supply` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn deposit_markup() -> Weight { + // Proof Size summary in bytes: + // Measured: `2260` + // Estimated: `260650` + // Minimum execution time: 820_796_000 picoseconds. + Weight::from_parts(831_306_000, 0) + .saturating_add(Weight::from_parts(0, 260650)) + .saturating_add(RocksDbWeight::get().reads(125)) + .saturating_add(RocksDbWeight::get().writes(118)) + } + /// Storage: `VeMinting::MarkupCoefficient` (r:1 w:0) + /// Proof: `VeMinting::MarkupCoefficient` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserMarkupInfos` (r:1 w:1) + /// Proof: `VeMinting::UserMarkupInfos` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::LockedTokens` (r:1 w:1) + /// Proof: `VeMinting::LockedTokens` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::TotalLock` (r:1 w:1) + /// Proof: `VeMinting::TotalLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tokens::Locks` (r:1 w:1) + /// Proof: `Tokens::Locks` (`max_values`: None, `max_size`: Some(1271), added: 3746, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:1 w:1) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(118), added: 2593, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::CurrencyMetadatas` (r:1 w:0) + /// Proof: `AssetRegistry::CurrencyMetadatas` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPositions` (r:1 w:0) + /// Proof: `VeMinting::UserPositions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Locked` (r:1 w:0) + /// Proof: `VeMinting::Locked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::IncentiveConfigs` (r:1 w:1) + /// Proof: `VeMinting::IncentiveConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Epoch` (r:1 w:1) + /// Proof: `VeMinting::Epoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::PointHistory` (r:1 w:105) + /// Proof: `VeMinting::PointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::SlopeChanges` (r:104 w:0) + /// Proof: `VeMinting::SlopeChanges` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointEpoch` (r:1 w:1) + /// Proof: `VeMinting::UserPointEpoch` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointHistory` (r:1 w:1) + /// Proof: `VeMinting::UserPointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Rewards` (r:1 w:1) + /// Proof: `VeMinting::Rewards` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserRewardPerTokenPaid` (r:1 w:1) + /// Proof: `VeMinting::UserRewardPerTokenPaid` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Supply` (r:1 w:0) + /// Proof: `VeMinting::Supply` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn withdraw_markup() -> Weight { + // Proof Size summary in bytes: + // Measured: `2725` + // Estimated: `261115` + // Minimum execution time: 813_232_000 picoseconds. + Weight::from_parts(823_401_000, 0) + .saturating_add(Weight::from_parts(0, 261115)) + .saturating_add(RocksDbWeight::get().reads(125)) + .saturating_add(RocksDbWeight::get().writes(118)) + } + /// Storage: `VeMinting::Locked` (r:1 w:1) + /// Proof: `VeMinting::Locked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VeMinting::Supply` (r:1 w:1) + /// Proof: `VeMinting::Supply` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPositions` (r:1 w:1) + /// Proof: `VeMinting::UserPositions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserLocked` (r:1 w:1) + /// Proof: `VeMinting::UserLocked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(118), added: 2593, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::CurrencyMetadatas` (r:1 w:0) + /// Proof: `AssetRegistry::CurrencyMetadatas` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::IncentiveConfigs` (r:1 w:1) + /// Proof: `VeMinting::IncentiveConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Epoch` (r:1 w:1) + /// Proof: `VeMinting::Epoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::PointHistory` (r:1 w:105) + /// Proof: `VeMinting::PointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::SlopeChanges` (r:104 w:0) + /// Proof: `VeMinting::SlopeChanges` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Rewards` (r:1 w:1) + /// Proof: `VeMinting::Rewards` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserRewardPerTokenPaid` (r:1 w:1) + /// Proof: `VeMinting::UserRewardPerTokenPaid` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointHistory` (r:0 w:1) + /// Proof: `VeMinting::UserPointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointEpoch` (r:0 w:1) + /// Proof: `VeMinting::UserPointEpoch` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn redeem_unlock() -> Weight { + // Proof Size summary in bytes: + // Measured: `2525` + // Estimated: `260915` + // Minimum execution time: 818_341_000 picoseconds. + Weight::from_parts(827_248_000, 0) + .saturating_add(Weight::from_parts(0, 260915)) + .saturating_add(RocksDbWeight::get().reads(122)) + .saturating_add(RocksDbWeight::get().writes(120)) + } + /// Storage: `VeMinting::MarkupCoefficient` (r:1 w:0) + /// Proof: `VeMinting::MarkupCoefficient` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VeMinting::LockedTokens` (r:2 w:1) + /// Proof: `VeMinting::LockedTokens` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserMarkupInfos` (r:1 w:1) + /// Proof: `VeMinting::UserMarkupInfos` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPositions` (r:1 w:0) + /// Proof: `VeMinting::UserPositions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Locked` (r:1 w:0) + /// Proof: `VeMinting::Locked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::IncentiveConfigs` (r:1 w:1) + /// Proof: `VeMinting::IncentiveConfigs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Epoch` (r:1 w:1) + /// Proof: `VeMinting::Epoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::PointHistory` (r:1 w:1) + /// Proof: `VeMinting::PointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointEpoch` (r:1 w:1) + /// Proof: `VeMinting::UserPointEpoch` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointHistory` (r:1 w:1) + /// Proof: `VeMinting::UserPointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Rewards` (r:1 w:1) + /// Proof: `VeMinting::Rewards` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserRewardPerTokenPaid` (r:1 w:1) + /// Proof: `VeMinting::UserRewardPerTokenPaid` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::SlopeChanges` (r:1 w:1) + /// Proof: `VeMinting::SlopeChanges` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Supply` (r:1 w:0) + /// Proof: `VeMinting::Supply` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn refresh() -> Weight { + // Proof Size summary in bytes: + // Measured: `1687` + // Estimated: `7627` + // Minimum execution time: 129_204_000 picoseconds. + Weight::from_parts(130_316_000, 0) + .saturating_add(Weight::from_parts(0, 7627)) + .saturating_add(RocksDbWeight::get().reads(16)) + .saturating_add(RocksDbWeight::get().writes(10)) } } diff --git a/pallets/vtoken-minting/Cargo.toml b/pallets/vtoken-minting/Cargo.toml index 81e8d76673..8c0d7de064 100644 --- a/pallets/vtoken-minting/Cargo.toml +++ b/pallets/vtoken-minting/Cargo.toml @@ -22,11 +22,12 @@ pallet-balances = { workspace = true } xcm = { workspace = true } cumulus-primitives-core = { workspace = true } sp-core = { workspace = true } +bifrost-ve-minting = { workspace = true } [dev-dependencies] orml-tokens = { workspace = true } bifrost-currencies = { workspace = true } -orml-xtokens = { workspace = true } +orml-xtokens = { workspace = true} orml-traits = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } @@ -50,6 +51,7 @@ std = [ "frame-benchmarking/std", "bifrost-primitives/std", "orml-traits/std", + "orml-xtokens/std", "bifrost-slp/std", "bifrost-asset-registry/std", "bifrost-runtime-common/std", diff --git a/pallets/vtoken-minting/src/benchmarking.rs b/pallets/vtoken-minting/src/benchmarking.rs index d0d20b4053..56691957e0 100644 --- a/pallets/vtoken-minting/src/benchmarking.rs +++ b/pallets/vtoken-minting/src/benchmarking.rs @@ -20,7 +20,7 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{Pallet as VtokenMinting, *}; -use bifrost_primitives::{CurrencyId, TokenSymbol}; +use bifrost_primitives::{CurrencyId, TokenSymbol, VKSM}; use frame_benchmarking::v1::{benchmarks, whitelisted_caller, BenchmarkError}; use frame_support::{assert_ok, sp_runtime::traits::UniqueSaturatedFrom}; use frame_system::RawOrigin; @@ -145,6 +145,83 @@ benchmarks! { let block_num =BlockNumberFor::::from(10u32); }:{VtokenMinting::::on_initialize(block_num);} + mint_with_lock { + let origin = T::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + let caller: T::AccountId = whitelisted_caller(); + const KSM: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); + let token_amount = BalanceOf::::unique_saturated_from(10000000000u128); + T::MultiCurrency::deposit(KSM, &caller, token_amount)?; + + pub const FEE: Permill = Permill::from_percent(5); + assert_ok!(VtokenMinting::::set_fees(RawOrigin::Root.into(), FEE, FEE)); + // Set minimum mint + assert_ok!(VtokenMinting::::set_minimum_mint(origin.clone(), KSM, BalanceOf::::unique_saturated_from(100u128))); + // set vtoken coefficient + assert_ok!(VtokenMinting::::set_incentive_coef(origin.clone(), VKSM, Some(1))); + // set incentive pool balance + assert_ok!(T::MultiCurrency::deposit( + VKSM, + &VtokenMinting::::incentive_pool_account(), + BalanceOf::::unique_saturated_from(100000000000000000000u128) + )); + // set incentive lock blocks + assert_ok!(VtokenMinting::::set_vtoken_incentive_lock_blocks( + origin.clone(), + VKSM, + Some(BlockNumberFor::::from(100u32)) + )); + }: _(RawOrigin::Signed(caller), KSM, token_amount,BoundedVec::default(), None) + + unlock_incentive_minted_vtoken { + let origin = T::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + let caller: T::AccountId = whitelisted_caller(); + const KSM: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); + let token_amount = BalanceOf::::unique_saturated_from(10000000000u128); + T::MultiCurrency::deposit(KSM, &caller, token_amount)?; + + pub const FEE: Permill = Permill::from_percent(5); + assert_ok!(VtokenMinting::::set_fees(RawOrigin::Root.into(), FEE, FEE)); + // Set minimum mint + assert_ok!(VtokenMinting::::set_minimum_mint(origin.clone(), KSM, BalanceOf::::unique_saturated_from(100u128))); + // set vtoken coefficient + assert_ok!(VtokenMinting::::set_incentive_coef(origin.clone(), VKSM, Some(1))); + // set incentive pool balance + assert_ok!(T::MultiCurrency::deposit( + VKSM, + &VtokenMinting::::incentive_pool_account(), + BalanceOf::::unique_saturated_from(100000000000000000000u128) + )); + // set incentive lock blocks + assert_ok!(VtokenMinting::::set_vtoken_incentive_lock_blocks( + origin.clone(), + VKSM, + Some(BlockNumberFor::::from(100u32)) + )); + // mint with lock + assert_ok!(VtokenMinting::::mint_with_lock( + RawOrigin::Signed(caller.clone()).into(), + KSM, + BalanceOf::::unique_saturated_from(10000000000u128), + BoundedVec::default(), + None + )); + + frame_system::Pallet::::set_block_number(BlockNumberFor::::from(101u32)); + + }: _(RawOrigin::Signed(caller), VKSM) + + set_incentive_coef { + let origin = T::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let coef = 1u128; + }: _(origin, VKSM, Some(coef)) + + set_vtoken_incentive_lock_blocks { + let origin = T::ControlOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let blocks = Some(BlockNumberFor::::from(1000u32)); + }: _(origin, VKSM, blocks) + impl_benchmark_test_suite!( VtokenMinting, crate::mock::ExtBuilder::default().one_hundred_for_alice_n_bob().build(), diff --git a/pallets/vtoken-minting/src/lib.rs b/pallets/vtoken-minting/src/lib.rs index 050f2b0dbd..e3140521d7 100644 --- a/pallets/vtoken-minting/src/lib.rs +++ b/pallets/vtoken-minting/src/lib.rs @@ -38,19 +38,21 @@ use bifrost_primitives::{ SlpxOperator, TimeUnit, VTokenMintRedeemProvider, VTokenSupplyProvider, VtokenMintingInterface, VtokenMintingOperator, }; +use bifrost_ve_minting::traits::VeMintingInterface; use frame_support::{ pallet_prelude::*, sp_runtime::{ traits::{ AccountIdConversion, CheckedAdd, CheckedSub, Saturating, UniqueSaturatedInto, Zero, }, - ArithmeticError, DispatchError, Permill, SaturatedConversion, + ArithmeticError, DispatchError, FixedU128, Permill, SaturatedConversion, }, + traits::LockIdentifier, transactional, BoundedVec, PalletId, }; use frame_system::pallet_prelude::*; use log; -use orml_traits::MultiCurrency; +use orml_traits::{MultiCurrency, MultiLockableCurrency}; pub use pallet::*; use sp_core::U256; use sp_std::{vec, vec::Vec}; @@ -66,6 +68,9 @@ pub type BalanceOf = <::MultiCurrency as MultiCurrency> + IsType<::RuntimeEvent>; - type MultiCurrency: MultiCurrency, CurrencyId = CurrencyId>; - // + MultiReservableCurrency, CurrencyId = CurrencyId>; + type MultiCurrency: MultiCurrency, CurrencyId = CurrencyId> + + MultiLockableCurrency>; /// The only origin that can edit token issuer list type ControlOrigin: EnsureOrigin; @@ -105,6 +110,10 @@ pub mod pallet { #[pallet::constant] type MaximumUnlockIdOfTimeUnit: Get; + // maximum unlocked vtoken records minted in an incentive mode + #[pallet::constant] + type MaxLockRecords: Get; + #[pallet::constant] type EntranceAccount: Get; @@ -114,6 +123,12 @@ pub mod pallet { #[pallet::constant] type FeeAccount: Get; + #[pallet::constant] + type RedeemFeeAccount: Get; + + #[pallet::constant] + type IncentivePoolAccount: Get; + #[pallet::constant] type RelayChainToken: Get; @@ -136,6 +151,14 @@ pub mod pallet { type BifrostSlpx: SlpxOperator>; + // veMinting interface + type VeMinting: VeMintingInterface< + AccountIdOf, + CurrencyIdOf, + BalanceOf, + BlockNumberFor, + >; + type CurrencyIdConversion: CurrencyIdConversion; type CurrencyIdRegister: CurrencyIdRegister; @@ -228,6 +251,21 @@ pub mod pallet { token_id: CurrencyIdOf, time_unit: TimeUnit, }, + IncentivizedMinting { + address: AccountIdOf, + token_id: CurrencyIdOf, + token_amount: BalanceOf, + locked_vtoken_amount: BalanceOf, + incentive_vtoken_amount: BalanceOf, + }, + VtokenIncentiveCoefSet { + vtoken_id: CurrencyIdOf, + coefficient: Option, + }, + VtokenIncentiveLockBlocksSet { + vtoken_id: CurrencyIdOf, + blocks: Option>, + }, } #[pallet::error] @@ -251,6 +289,15 @@ pub mod pallet { TooManyRedeems, CanNotRedeem, CanNotRebond, + NotEnoughBalance, + VeBNCCheckingError, + IncentiveCoefNotFound, + TooManyLocks, + ConvertError, + NoUnlockRecord, + FailToRemoveLock, + BalanceZero, + IncentiveLockBlocksNotSet, } #[pallet::storage] @@ -339,6 +386,32 @@ pub mod pallet { #[pallet::getter(fn hook_iteration_limit)] pub type HookIterationLimit = StorageValue<_, u32, ValueQuery>; + //【vtoken -> Blocks】, the locked blocks for each vtoken when minted in an incentive mode + #[pallet::storage] + #[pallet::getter(fn get_mint_with_lock_blocks)] + pub type MintWithLockBlocks = + StorageMap<_, Blake2_128Concat, CurrencyId, BlockNumberFor>; + + //【vtoken -> incentive coefficient】,the incentive coefficient for each vtoken when minted in + // an incentive mode + #[pallet::storage] + #[pallet::getter(fn get_vtoken_incentive_coef)] + pub type VtokenIncentiveCoef = StorageMap<_, Blake2_128Concat, CurrencyId, u128>; + + //【user + vtoken -> (total_locked, vec[(locked_amount, due_block_num)])】, the locked vtoken + // records for each user + #[pallet::storage] + #[pallet::getter(fn get_vtoken_lock_ledger)] + pub type VtokenLockLedger = StorageDoubleMap< + _, + Blake2_128Concat, + AccountIdOf, + Blake2_128Concat, + CurrencyId, + (BalanceOf, BoundedVec<(BalanceOf, BlockNumberFor), T::MaxLockRecords>), + OptionQuery, + >; + #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { @@ -829,6 +902,208 @@ pub mod pallet { Self::deposit_event(Event::CurrencyTimeUnitRecreated { token_id, time_unit }); Ok(()) } + + // mint with lock to get incentive vtoken + #[pallet::call_index(14)] + #[pallet::weight(T::WeightInfo::mint_with_lock())] + pub fn mint_with_lock( + origin: OriginFor, + token_id: CurrencyIdOf, + token_amount: BalanceOf, + remark: BoundedVec>, + channel_id: Option, + ) -> DispatchResult { + // Check origin + let minter = ensure_signed(origin)?; + + // check if the minter has at least token_amount of token_id which is transferable + T::MultiCurrency::ensure_can_withdraw(token_id, &minter, token_amount) + .map_err(|_| Error::::NotEnoughBalance)?; + + // check whether the token_id is supported + ensure!(MinimumMint::::contains_key(token_id), Error::::NotSupportTokenType); + + // check whether the user has veBNC + let vebnc_balance = T::VeMinting::balance_of(&minter, None) + .map_err(|_| Error::::VeBNCCheckingError)?; + ensure!(vebnc_balance > BalanceOf::::zero(), Error::::NotEnoughBalance); + + // check whether the vtoken coefficient is set + let vtoken_id = T::CurrencyIdConversion::convert_to_vtoken(token_id) + .map_err(|_| Error::::NotSupportTokenType)?; + + ensure!( + VtokenIncentiveCoef::::contains_key(vtoken_id), + Error::::IncentiveCoefNotFound + ); + + // check whether the pool has balance of vtoken_id + let incentive_pool_account = &Self::incentive_pool_account(); + let vtoken_pool_balance = + T::MultiCurrency::free_balance(vtoken_id, &incentive_pool_account); + + ensure!(vtoken_pool_balance > BalanceOf::::zero(), Error::::NotEnoughBalance); + + // mint vtoken + let vtoken_minted = + Self::mint_inner(minter.clone(), token_id, token_amount, remark, channel_id)?; + + // lock vtoken and record the lock + Self::lock_vtoken_for_incentive_minting(minter.clone(), vtoken_id, vtoken_minted)?; + + // calculate the incentive amount + let incentive_amount = + Self::calculate_incentive_vtoken_amount(&minter, vtoken_id, vtoken_minted)?; + + // Since the user has already locked the vtoken, we can directly transfer the incentive + // vtoken. It won't fail. transfer the incentive amount to the minter + T::MultiCurrency::transfer( + vtoken_id, + incentive_pool_account, + &minter, + incentive_amount, + ) + .map_err(|_| Error::::NotEnoughBalance)?; + + // deposit event + Self::deposit_event(Event::IncentivizedMinting { + address: minter, + token_id, + token_amount, + locked_vtoken_amount: vtoken_minted, + incentive_vtoken_amount: incentive_amount, + }); + + Ok(()) + } + + #[pallet::call_index(15)] + #[pallet::weight(T::WeightInfo::unlock_incentive_minted_vtoken())] + pub fn unlock_incentive_minted_vtoken( + origin: OriginFor, + vtoken_id: CurrencyIdOf, + ) -> DispatchResult { + let unlocker = ensure_signed(origin)?; + + // get the user's VtokenLockLedger + ensure!( + VtokenLockLedger::::contains_key(&unlocker, vtoken_id), + Error::::UserUnlockLedgerNotFound + ); + + VtokenLockLedger::::mutate_exists( + &unlocker, + vtoken_id, + |maybe_ledger| -> Result<(), Error> { + let current_block = frame_system::Pallet::::block_number(); + + if let Some(ref mut ledger) = maybe_ledger { + // check the total locked amount + let (total_locked, mut lock_records) = ledger.clone(); + + // unlock the vtoken + let mut unlock_amount = BalanceOf::::zero(); + let mut remove_index = 0; + + // enumerate lock_records + for (index, (locked_amount, due_block_num)) in + lock_records.iter().enumerate() + { + if current_block >= *due_block_num { + unlock_amount += *locked_amount; + remove_index = index + 1; + } else { + break; + } + } + + // remove all the records less than remove_index + if remove_index > 0 { + lock_records.drain(0..remove_index); + } + + // check the unlock amount + ensure!(unlock_amount > BalanceOf::::zero(), Error::::NoUnlockRecord); + + let remaining_locked_amount = total_locked + .checked_sub(&unlock_amount) + .ok_or(Error::::CalculationOverflow)?; + + if remaining_locked_amount == BalanceOf::::zero() { + T::MultiCurrency::remove_lock(INCENTIVE_LOCK_ID, vtoken_id, &unlocker) + .map_err(|_| Error::::FailToRemoveLock)?; + + // remove the ledger + *maybe_ledger = None; + } else { + // update the ledger + *ledger = (remaining_locked_amount, lock_records); + + // reset the locked amount to be remaining_locked_amount + T::MultiCurrency::set_lock( + INCENTIVE_LOCK_ID, + vtoken_id, + &unlocker, + remaining_locked_amount, + ) + .map_err(|_| Error::::Unexpected)?; + } + + Ok(()) + } else { + Err(Error::::UserUnlockLedgerNotFound) + } + }, + )?; + + Ok(()) + } + + #[pallet::call_index(16)] + #[pallet::weight(T::WeightInfo::set_incentive_coef())] + pub fn set_incentive_coef( + origin: OriginFor, + vtoken_id: CurrencyIdOf, + new_coef_op: Option, + ) -> DispatchResult { + T::ControlOrigin::ensure_origin(origin)?; + + if let Some(new_coef) = new_coef_op { + VtokenIncentiveCoef::::insert(vtoken_id, new_coef); + } else { + VtokenIncentiveCoef::::remove(vtoken_id); + } + + Self::deposit_event(Event::VtokenIncentiveCoefSet { + vtoken_id, + coefficient: new_coef_op, + }); + + Ok(()) + } + + #[pallet::call_index(17)] + #[pallet::weight(T::WeightInfo::set_vtoken_incentive_lock_blocks())] + pub fn set_vtoken_incentive_lock_blocks( + origin: OriginFor, + vtoken_id: CurrencyIdOf, + new_blockes_op: Option>, + ) -> DispatchResult { + T::ControlOrigin::ensure_origin(origin)?; + + if let Some(new_blocks) = new_blockes_op { + MintWithLockBlocks::::insert(vtoken_id, new_blocks); + } else { + MintWithLockBlocks::::remove(vtoken_id); + } + + Self::deposit_event(Event::VtokenIncentiveLockBlocksSet { + vtoken_id, + blocks: new_blockes_op, + }); + + Ok(()) + } } impl Pallet { @@ -1335,7 +1610,12 @@ pub mod pallet { let vtoken_amount = vtoken_amount.checked_sub(&redeem_fee).ok_or(Error::::CalculationOverflow)?; // Charging fees - T::MultiCurrency::transfer(vtoken_id, &exchanger, &T::FeeAccount::get(), redeem_fee)?; + T::MultiCurrency::transfer( + vtoken_id, + &exchanger, + &T::RedeemFeeAccount::get(), + redeem_fee, + )?; let token_pool_amount = Self::token_pool(token_id); let vtoken_total_issuance = T::MultiCurrency::total_issuance(vtoken_id); @@ -1510,6 +1790,127 @@ pub mod pallet { pub fn token_id_inner(vtoken_id: CurrencyIdOf) -> Option> { T::CurrencyIdConversion::convert_to_token(vtoken_id).ok() } + + pub fn incentive_pool_account() -> AccountIdOf { + T::IncentivePoolAccount::get().into_account_truncating() + } + + // to lock user vtoken for incentive minting + fn lock_vtoken_for_incentive_minting( + minter: AccountIdOf, + vtoken_id: CurrencyIdOf, + vtoken_amount: BalanceOf, + ) -> Result<(), Error> { + // first, lock the vtoken + // second, record the lock in ledger + + // check whether the minter has enough vtoken + T::MultiCurrency::ensure_can_withdraw(vtoken_id, &minter, vtoken_amount) + .map_err(|_| Error::::NotEnoughBalance)?; + + // new amount that should be locked + let mut new_lock_total = vtoken_amount; + + // check the previous locked amount under the same vtoken_id from ledger + // and revise ledger to set the new_amount to be previous_amount + vtoken_amount + VtokenLockLedger::::mutate_exists( + &minter, + &vtoken_id, + |value| -> Result<(), Error> { + // get the vtoken lock duration from VtokenIncentiveCoef + let lock_duration = Self::get_mint_with_lock_blocks(vtoken_id) + .ok_or(Error::::IncentiveLockBlocksNotSet)?; + let current_block = frame_system::Pallet::::block_number(); + let due_block = current_block + .checked_add(&lock_duration) + .ok_or(Error::::CalculationOverflow)?; + + if let Some(ref mut ledger) = value { + new_lock_total = ledger + .0 + .checked_add(&vtoken_amount) + .ok_or(Error::::CalculationOverflow)?; + + ledger.0 = new_lock_total; + + // push new item to the boundedvec of the ledger + ledger + .1 + .try_push((vtoken_amount, due_block)) + .map_err(|_| Error::::TooManyLocks)?; + } else { + let item = BoundedVec::try_from(vec![(vtoken_amount, due_block)]) + .map_err(|_| Error::::ConvertError)?; + + *value = Some((vtoken_amount, item)); + } + Ok(()) + }, + )?; + + // extend the locked amount to be new_lock_total + T::MultiCurrency::set_lock(INCENTIVE_LOCK_ID, vtoken_id, &minter, new_lock_total) + .map_err(|_| Error::::NotEnoughBalance)?; + + Ok(()) + } + + fn calculate_incentive_vtoken_amount( + minter: &AccountIdOf, + vtoken_id: CurrencyIdOf, + vtoken_amount: BalanceOf, + ) -> Result, Error> { + // get the vtoken pool balance + let vtoken_pool_balance = + T::MultiCurrency::free_balance(vtoken_id, &Self::incentive_pool_account()); + ensure!(vtoken_pool_balance > BalanceOf::::zero(), Error::::NotEnoughBalance); + + // get current block number + let current_block_number: BlockNumberFor = frame_system::Pallet::::block_number(); + // get the veBNC total amount + let vebnc_total_issuance = T::VeMinting::total_supply(current_block_number) + .map_err(|_| Error::::VeBNCCheckingError)?; + ensure!(vebnc_total_issuance > BalanceOf::::zero(), Error::::BalanceZero); + + // get the veBNC balance of the minter + let minter_vebnc_balance = T::VeMinting::balance_of(minter, None) + .map_err(|_| Error::::VeBNCCheckingError)?; + ensure!(minter_vebnc_balance > BalanceOf::::zero(), Error::::NotEnoughBalance); + + // get the percentage of the veBNC balance of the minter to the total veBNC amount and + // get the square root of the percentage + let percentage = Permill::from_rational(minter_vebnc_balance, vebnc_total_issuance); + let sqrt_percentage = + FixedU128::from_inner(percentage * 1_000_000_000_000_000_000u128).sqrt(); + let percentage = Permill::from_rational( + sqrt_percentage.into_inner(), + 1_000_000_000_000_000_000u128.into(), + ); + // get the total issuance of the vtoken + let vtoken_total_issuance = T::MultiCurrency::total_issuance(vtoken_id); + + // get the incentive coef for the vtoken + let incentive_coef = Self::get_vtoken_incentive_coef(vtoken_id) + .ok_or(Error::::IncentiveCoefNotFound)?; + + // calculate the incentive amount, but mind the overflow + // incentive_amount = vtoken_pool_balance * incentive_coef * vtoken_amount * + // sqrt_percentage / vtoken_total_issuance + let incentive_amount = + U256::from(percentage.mul_ceil(vtoken_pool_balance).saturated_into::()) + .checked_mul(U256::from(incentive_coef)) + .and_then(|x| x.checked_mul(U256::from(vtoken_amount.saturated_into::()))) + // .and_then(|x| x.checked_mul(percentage)) + .and_then(|x| { + x.checked_div(U256::from(vtoken_total_issuance.saturated_into::())) + }) + // first turn into u128,then use unique_saturated_into BalanceOf + .map(|x| x.saturated_into::()) + .map(|x| x.unique_saturated_into()) + .ok_or(Error::::CalculationOverflow)?; + + Ok(incentive_amount) + } } } diff --git a/pallets/vtoken-minting/src/mock.rs b/pallets/vtoken-minting/src/mock.rs index b1d4cf64b5..9fcdf2d502 100644 --- a/pallets/vtoken-minting/src/mock.rs +++ b/pallets/vtoken-minting/src/mock.rs @@ -28,6 +28,7 @@ use bifrost_primitives::{ }; use bifrost_runtime_common::{micro, milli}; use bifrost_slp::{QueryId, QueryResponseManager}; +use bifrost_ve_minting::{Point, VeMintingInterface}; pub use cumulus_primitives_core::ParaId; use frame_support::{ derive_impl, ord_parameter_types, @@ -43,7 +44,7 @@ use parity_scale_codec::{Decode, Encode}; use sp_core::blake2_256; use sp_runtime::{ traits::{AccountIdConversion, ConstU32, Convert, IdentityLookup, TrailingZeroInput}, - AccountId32, BuildStorage, + AccountId32, BuildStorage, DispatchError, DispatchResult, }; use xcm::{prelude::*, v3::Weight}; use xcm_builder::{FixedWeightBounds, FrameTransactionalProcessor}; @@ -157,7 +158,7 @@ impl orml_tokens::Config for Runtime { type DustRemovalWhitelist = Nothing; type RuntimeEvent = RuntimeEvent; type ExistentialDeposits = ExistentialDeposits; - type MaxLocks = (); + type MaxLocks = ConstU32<50>; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type WeightInfo = (); @@ -196,8 +197,10 @@ impl orml_xtokens::Config for Runtime { parameter_types! { pub const MaximumUnlockIdOfUser: u32 = 1_000; pub const MaximumUnlockIdOfTimeUnit: u32 = 1_000; + pub const MaxLockRecords: u32 = 64; pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); } @@ -212,11 +215,15 @@ impl vtoken_minting::Config for Runtime { type ControlOrigin = EnsureSignedBy; type MaximumUnlockIdOfUser = MaximumUnlockIdOfUser; type MaximumUnlockIdOfTimeUnit = MaximumUnlockIdOfTimeUnit; + type MaxLockRecords = MaxLockRecords; type EntranceAccount = BifrostEntranceAccount; type ExitAccount = BifrostExitAccount; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; + type IncentivePoolAccount = IncentivePoolAccount; type BifrostSlp = Slp; type BifrostSlpx = SlpxInterface; + type VeMinting = VeMinting; type RelayChainToken = RelayCurrencyId; type CurrencyIdConversion = AssetIdMaps; type CurrencyIdRegister = AssetIdMaps; @@ -490,7 +497,7 @@ impl ExtBuilder { } /// Run until a particular block. -pub fn _run_to_block(n: BlockNumber) { +pub fn run_to_block(n: BlockNumber) { use frame_support::traits::Hooks; while System::block_number() <= n { VtokenMinting::on_finalize(System::block_number()); @@ -500,3 +507,92 @@ pub fn _run_to_block(n: BlockNumber) { VtokenMinting::on_initialize(System::block_number()); } } + +use bifrost_primitives::PoolId; +use bifrost_ve_minting::IncentiveConfig; +// Mock VeMinting Struct +pub struct VeMinting; +impl VeMintingInterface for VeMinting { + fn balance_of(_addr: &AccountId, _time: Option) -> Result { + Ok(100) + } + + fn total_supply(_t: BlockNumber) -> Result { + Ok(10000) + } + + fn increase_amount_inner(_who: &AccountId, _position: u128, _value: Balance) -> DispatchResult { + Ok(()) + } + + fn deposit_for(_who: &AccountId, _position: u128, _value: Balance) -> DispatchResult { + Ok(()) + } + + fn withdraw_inner(_who: &AccountId, _position: u128) -> DispatchResult { + Ok(()) + } + + fn supply_at(_: Point, _: u64) -> Result { + todo!() + } + + fn find_block_epoch(_: u64, _: sp_core::U256) -> sp_core::U256 { + todo!() + } + + fn create_lock_inner( + _: &sp_runtime::AccountId32, + _: u128, + _: u64, + ) -> Result<(), sp_runtime::DispatchError> { + todo!() + } + + fn increase_unlock_time_inner( + _: &sp_runtime::AccountId32, + _: u128, + _: u64, + ) -> Result<(), sp_runtime::DispatchError> { + todo!() + } + + fn auto_notify_reward( + _: u32, + _: u64, + _: Vec<(CurrencyId, Balance)>, + ) -> Result<(), sp_runtime::DispatchError> { + todo!() + } + + fn update_reward( + _pool_id: PoolId, + _addr: Option<&AccountId>, + _share_info: Option<(Balance, Balance)>, + ) -> DispatchResult { + Ok(()) + } + + fn get_rewards( + _pool_id: PoolId, + _addr: &AccountId, + _share_info: Option<(Balance, Balance)>, + ) -> DispatchResult { + Ok(()) + } + + fn set_incentive( + _pool_id: PoolId, + _rewards_duration: Option, + _incentive_controller: Option, + ) { + } + fn add_reward( + _addr: &AccountId, + _conf: &mut IncentiveConfig, + _rewards: &Vec<(CurrencyId, Balance)>, + _remaining: Balance, + ) -> DispatchResult { + Ok(()) + } +} diff --git a/pallets/vtoken-minting/src/tests.rs b/pallets/vtoken-minting/src/tests.rs index 55825f5089..9f7c4015ea 100644 --- a/pallets/vtoken-minting/src/tests.rs +++ b/pallets/vtoken-minting/src/tests.rs @@ -20,9 +20,10 @@ #![cfg(test)] -use crate::{mock::*, *}; +use crate::{mock::*, DispatchError::Module, *}; use bifrost_primitives::currency::{BNC, FIL, KSM, MOVR, VBNC, VFIL, VKSM, VMOVR}; use frame_support::{assert_noop, assert_ok, sp_runtime::Permill, BoundedVec}; +use sp_runtime::ModuleError; #[test] fn mint_bnc() { @@ -508,3 +509,222 @@ fn recreate_currency_ongoing_time_unit_should_work() { assert_eq!(VtokenMinting::ongoing_time_unit(KSM), Some(TimeUnit::Round(2))); }) } + +#[test] +fn mint_with_lock_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + env_logger::try_init().unwrap_or(()); + + pub const FEE: Permill = Permill::from_percent(5); + assert_ok!(VtokenMinting::set_fees(RuntimeOrigin::root(), FEE, FEE)); + + // mint exceeds bob's KSM balance + assert_noop!( + VtokenMinting::mint_with_lock( + Some(BOB).into(), + KSM, + 10000000000000, + BoundedVec::default(), + None + ), + Error::::NotEnoughBalance + ); + + // Minimum Mint not set + assert_noop!( + VtokenMinting::mint_with_lock(Some(BOB).into(), KSM, 100, BoundedVec::default(), None), + Error::::NotSupportTokenType + ); + + // Set minimum mint + assert_ok!(VtokenMinting::set_minimum_mint(RuntimeOrigin::signed(ALICE), KSM, 100)); + + // vtoken coefficient not set + assert_noop!( + VtokenMinting::mint_with_lock(Some(BOB).into(), KSM, 100, BoundedVec::default(), None), + Error::::IncentiveCoefNotFound + ); + + // set vtoken coefficient + assert_ok!(VtokenMinting::set_incentive_coef(RuntimeOrigin::signed(ALICE), VKSM, Some(1))); + + // pool not enough vKSM balance + assert_noop!( + VtokenMinting::mint_with_lock(Some(BOB).into(), KSM, 100, BoundedVec::default(), None), + Error::::NotEnoughBalance + ); + + // set incentive pool balance + assert_ok!(Tokens::deposit( + VKSM, + &VtokenMinting::incentive_pool_account(), + 100000000000000000000 + )); + + // incentive lock blocks not set + assert_noop!( + VtokenMinting::mint_with_lock(Some(BOB).into(), KSM, 100, BoundedVec::default(), None), + Error::::IncentiveLockBlocksNotSet + ); + + // set incentive lock blocks + assert_ok!(VtokenMinting::set_vtoken_incentive_lock_blocks( + RuntimeOrigin::signed(ALICE), + VKSM, + Some(100) + )); + + let bob_old_balance = Tokens::free_balance(VKSM, &BOB); + // mint with lock + assert_ok!(VtokenMinting::mint_with_lock( + Some(BOB).into(), + KSM, + 100000000000, + BoundedVec::default(), + None + )); + + // check the vksm balance of bob. Should be minted_amount + incentive amount + original + // balance + assert_eq!(Tokens::free_balance(VKSM, &BOB), 95000000000 + 9499999990 + bob_old_balance); + + // check the pool balance, should have been transferred 9499999990 to Bob account + assert_eq!( + Tokens::free_balance(VKSM, &VtokenMinting::incentive_pool_account()), + 100000000000000000000 - 9499999990 + ); + + // check ledger + let lock_ledger = VtokenMinting::get_vtoken_lock_ledger(BOB, VKSM).unwrap(); + let list = BoundedVec::try_from(vec![(95000000000u128, 100u64)]).unwrap(); + let should_be_ledger = (95000000000u128, list); + assert_eq!(lock_ledger, should_be_ledger); + }) +} + +#[test] +fn unlock_incentive_minted_vtoken_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + env_logger::try_init().unwrap_or(()); + + pub const FEE: Permill = Permill::from_percent(5); + assert_ok!(VtokenMinting::set_fees(RuntimeOrigin::root(), FEE, FEE)); + // Set minimum mint + assert_ok!(VtokenMinting::set_minimum_mint(RuntimeOrigin::signed(ALICE), KSM, 100)); + // set vtoken coefficient + assert_ok!(VtokenMinting::set_incentive_coef(RuntimeOrigin::signed(ALICE), VKSM, Some(1))); + // set incentive pool balance + assert_ok!(Tokens::deposit( + VKSM, + &VtokenMinting::incentive_pool_account(), + 100000000000000000000 + )); + // set incentive lock blocks + assert_ok!(VtokenMinting::set_vtoken_incentive_lock_blocks( + RuntimeOrigin::signed(ALICE), + VKSM, + Some(100) + )); + // mint with lock + assert_ok!(VtokenMinting::mint_with_lock( + Some(BOB).into(), + KSM, + 100000000000, + BoundedVec::default(), + None + )); + + run_to_block(101); + + // check ledger + let lock_ledger = VtokenMinting::get_vtoken_lock_ledger(BOB, VKSM).unwrap(); + let list = BoundedVec::try_from(vec![(95000000000u128, 100u64)]).unwrap(); + let should_be_ledger = (95000000000u128, list); + assert_eq!(lock_ledger, should_be_ledger); + + let bob_vksm_balance = Tokens::free_balance(VKSM, &BOB); + // Bob's account cannot withdraw the locked vksm + assert_eq!( + ::MultiCurrency::ensure_can_withdraw( + VKSM, + &BOB, + bob_vksm_balance + ), + Err(Module(ModuleError { + index: 1, + error: [2, 0, 0, 0,], + message: Some("LiquidityRestrictions",), + },),) + ); + + // unlock incentive minted vtoken + assert_ok!(VtokenMinting::unlock_incentive_minted_vtoken(RuntimeOrigin::signed(BOB), VKSM)); + + // Bob's amount can withdraw the locked vksm now + assert_ok!(::MultiCurrency::ensure_can_withdraw( + VKSM, + &BOB, + bob_vksm_balance + )); + + // total amount should be remain the same + let new_bob_vksm_balance = Tokens::free_balance(VKSM, &BOB); + assert_eq!(new_bob_vksm_balance, bob_vksm_balance); + + // check ledger + let lock_ledger = VtokenMinting::get_vtoken_lock_ledger(BOB, VKSM); + assert_eq!(lock_ledger, None); + }) +} + +#[test] +fn set_incentive_coef_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + env_logger::try_init().unwrap_or(()); + + // get vksm coefficient should return None + assert_eq!(VtokenMinting::get_vtoken_incentive_coef(VKSM), None); + + // set vksm coefficient + assert_ok!(VtokenMinting::set_incentive_coef(RuntimeOrigin::signed(ALICE), VKSM, Some(1))); + + // get vksm coefficient should return Some(1) + assert_eq!(VtokenMinting::get_vtoken_incentive_coef(VKSM), Some(1)); + + // set vksm coefficient to None + assert_ok!(VtokenMinting::set_incentive_coef(RuntimeOrigin::signed(ALICE), VKSM, None)); + + // get vksm coefficient should return None + assert_eq!(VtokenMinting::get_vtoken_incentive_coef(VKSM), None); + }) +} + +#[test] +fn set_vtoken_incentive_lock_blocks_should_work() { + ExtBuilder::default().one_hundred_for_alice_n_bob().build().execute_with(|| { + env_logger::try_init().unwrap_or(()); + + // get vksm lock blocks should return None + assert_eq!(VtokenMinting::get_mint_with_lock_blocks(VKSM), None); + + // set vksm lock blocks + assert_ok!(VtokenMinting::set_vtoken_incentive_lock_blocks( + RuntimeOrigin::signed(ALICE), + VKSM, + Some(100) + )); + + // get vksm lock blocks should return Some(100) + assert_eq!(VtokenMinting::get_mint_with_lock_blocks(VKSM), Some(100)); + + // set vksm lock blocks to None + assert_ok!(VtokenMinting::set_vtoken_incentive_lock_blocks( + RuntimeOrigin::signed(ALICE), + VKSM, + None + )); + + // get vksm lock blocks should return None + assert_eq!(VtokenMinting::get_mint_with_lock_blocks(VKSM), None); + }) +} diff --git a/pallets/vtoken-minting/src/weights.rs b/pallets/vtoken-minting/src/weights.rs index ef949d1f7b..68f316aaf7 100644 --- a/pallets/vtoken-minting/src/weights.rs +++ b/pallets/vtoken-minting/src/weights.rs @@ -64,10 +64,14 @@ pub trait WeightInfo { fn set_fees() -> Weight; fn set_hook_iteration_limit() -> Weight; fn mint() -> Weight; + fn mint_with_lock() -> Weight; fn redeem() -> Weight; fn rebond() -> Weight; fn rebond_by_unlock_id() -> Weight; fn on_initialize() -> Weight; + fn unlock_incentive_minted_vtoken() -> Weight; + fn set_incentive_coef() -> Weight; + fn set_vtoken_incentive_lock_blocks() -> Weight; } // For backwards compatibility and tests @@ -210,6 +214,17 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } + + fn mint_with_lock() -> Weight { + // Proof Size summary in bytes: + // Measured: `2041` + // Estimated: `8769` + // Minimum execution time: 316_607_000 picoseconds. + Weight::from_parts(325_143_000, 8769) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: VtokenMinting MinimumRedeem (r:1 w:0) /// Proof: VtokenMinting MinimumRedeem (max_values: None, max_size: Some(38), added: 2513, mode: MaxEncodedLen) /// Storage: Slp DelegationsOccupied (r:1 w:0) @@ -317,4 +332,22 @@ impl WeightInfo for () { Weight::from_parts(15_243_000, 3492) .saturating_add(RocksDbWeight::get().reads(1_u64)) } + + fn unlock_incentive_minted_vtoken() -> Weight { + Weight::from_parts(227_544_000, 8769) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + + fn set_incentive_coef() -> Weight { + Weight::from_parts(44_736_000, 1493) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + + fn set_vtoken_incentive_lock_blocks() -> Weight { + Weight::from_parts(44_736_000, 1493) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/runtime/bifrost-kusama/src/lib.rs b/runtime/bifrost-kusama/src/lib.rs index 9307cac797..8fbdff9f30 100644 --- a/runtime/bifrost-kusama/src/lib.rs +++ b/runtime/bifrost-kusama/src/lib.rs @@ -339,6 +339,8 @@ parameter_types! { pub const OraclePalletId: PalletId = PalletId(*b"bf/oracl"); pub const StableAssetPalletId: PalletId = PalletId(*b"bf/stabl"); pub const CommissionPalletId: PalletId = PalletId(*b"bf/comms"); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); + pub const FarmingGaugeRewardIssuerPalletId: PalletId = PalletId(*b"bf/fmgar"); } impl frame_system::Config for Runtime { @@ -1464,6 +1466,7 @@ impl bifrost_farming::Config for Runtime { type VeMinting = (); type BlockNumberToBalance = ConvertInto; type WhitelistMaximumLimit = WhitelistMaximumLimit; + type GaugeRewardIssuer = FarmingGaugeRewardIssuerPalletId; } parameter_types! { @@ -1663,6 +1666,7 @@ impl bifrost_vtoken_minting::Config for Runtime { type EntranceAccount = SlpEntrancePalletId; type ExitAccount = SlpExitPalletId; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type BifrostSlp = Slp; type BifrostSlpx = Slpx; type WeightInfo = weights::bifrost_vtoken_minting::BifrostWeight; @@ -1677,6 +1681,9 @@ impl bifrost_vtoken_minting::Config for Runtime { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2092>; type ChannelCommission = ChannelCommission; + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); } impl bifrost_slpx::Config for Runtime { diff --git a/runtime/bifrost-kusama/src/weights/bifrost_vtoken_minting.rs b/runtime/bifrost-kusama/src/weights/bifrost_vtoken_minting.rs index 01106010bc..c53ec8bb6b 100644 --- a/runtime/bifrost-kusama/src/weights/bifrost_vtoken_minting.rs +++ b/runtime/bifrost-kusama/src/weights/bifrost_vtoken_minting.rs @@ -299,4 +299,28 @@ impl bifrost_vtoken_minting::WeightInfo for BifrostWeig Weight::from_parts(16_763_000, 3492) .saturating_add(T::DbWeight::get().reads(1)) } + + fn mint_with_lock() -> Weight { + Weight::from_parts(325_143_000, 8769) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + + fn unlock_incentive_minted_vtoken() -> Weight { + Weight::from_parts(227_544_000, 8769) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + + fn set_incentive_coef() -> Weight { + Weight::from_parts(44_736_000, 1493) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + + fn set_vtoken_incentive_lock_blocks() -> Weight { + Weight::from_parts(44_736_000, 1493) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/runtime/bifrost-polkadot/src/lib.rs b/runtime/bifrost-polkadot/src/lib.rs index 9937690d81..0fffbc8fca 100644 --- a/runtime/bifrost-polkadot/src/lib.rs +++ b/runtime/bifrost-polkadot/src/lib.rs @@ -324,6 +324,8 @@ parameter_types! { pub const StableAssetPalletId: PalletId = PalletId(*b"bf/stabl"); pub const CommissionPalletId: PalletId = PalletId(*b"bf/comms"); pub const CloudsPalletId: PalletId = PalletId(*b"bf/cloud"); + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); + pub const FarmingGaugeRewardIssuerPalletId: PalletId = PalletId(*b"bf/fmgar"); } impl frame_system::Config for Runtime { @@ -1296,6 +1298,7 @@ impl bifrost_farming::Config for Runtime { type VeMinting = VeMinting; type BlockNumberToBalance = ConvertInto; type WhitelistMaximumLimit = WhitelistMaximumLimit; + type GaugeRewardIssuer = FarmingGaugeRewardIssuerPalletId; } parameter_types! { @@ -1498,6 +1501,7 @@ impl bifrost_vtoken_minting::Config for Runtime { type EntranceAccount = SlpEntrancePalletId; type ExitAccount = SlpExitPalletId; type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; type BifrostSlp = Slp; type BifrostSlpx = Slpx; type WeightInfo = weights::bifrost_vtoken_minting::BifrostWeight; @@ -1512,20 +1516,25 @@ impl bifrost_vtoken_minting::Config for Runtime { type MantaParachainId = ConstU32<2104>; type InterlayParachainId = ConstU32<2032>; type ChannelCommission = ChannelCommission; + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = VeMinting; } parameter_types! { pub const VeMintingTokenType: CurrencyId = CurrencyId::VToken(TokenSymbol::BNC); - pub const Week: BlockNumber = WEEKS; + pub const Week: BlockNumber = 10; pub const MaxBlock: BlockNumber = 4 * 365 * DAYS; pub const Multiplier: Balance = 10_u128.pow(12); - pub const VoteWeightMultiplier: Balance = 3; + pub const VoteWeightMultiplier: Balance = 1; + pub const MaxPositions: u32 = 10; + pub const MarkupRefreshLimit: u32 = 100; } impl bifrost_ve_minting::Config for Runtime { type RuntimeEvent = RuntimeEvent; type MultiCurrency = Currencies; - type ControlOrigin = EitherOfDiverse; + type ControlOrigin = TechAdminOrCouncil; type TokenType = VeMintingTokenType; type VeMintingPalletId = VeMintingPalletId; type IncentivePalletId = IncentivePalletId; @@ -1535,6 +1544,8 @@ impl bifrost_ve_minting::Config for Runtime { type MaxBlock = MaxBlock; type Multiplier = Multiplier; type VoteWeightMultiplier = VoteWeightMultiplier; + type MaxPositions = MaxPositions; + type MarkupRefreshLimit = MarkupRefreshLimit; } parameter_types! { diff --git a/runtime/bifrost-polkadot/src/weights/bifrost_ve_minting.rs b/runtime/bifrost-polkadot/src/weights/bifrost_ve_minting.rs index b618c420ba..ae92ae1287 100644 --- a/runtime/bifrost-polkadot/src/weights/bifrost_ve_minting.rs +++ b/runtime/bifrost-polkadot/src/weights/bifrost_ve_minting.rs @@ -311,4 +311,216 @@ impl bifrost_ve_minting::WeightInfo for BifrostWeight Weight { + // Proof Size summary in bytes: + // Measured: `213` + // Estimated: `3678` + // Minimum execution time: 12_353_000 picoseconds. + Weight::from_parts(12_533_000, 0) + .saturating_add(Weight::from_parts(0, 3678)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `VeMinting::MarkupCoefficient` (r:1 w:0) + /// Proof: `VeMinting::MarkupCoefficient` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::TotalLock` (r:1 w:1) + /// Proof: `VeMinting::TotalLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VeMinting::UserMarkupInfos` (r:1 w:1) + /// Proof: `VeMinting::UserMarkupInfos` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::LockedTokens` (r:1 w:1) + /// Proof: `VeMinting::LockedTokens` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tokens::Locks` (r:1 w:1) + /// Proof: `Tokens::Locks` (`max_values`: None, `max_size`: Some(1271), added: 3746, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:1 w:1) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(118), added: 2593, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::CurrencyMetadatas` (r:1 w:0) + /// Proof: `AssetRegistry::CurrencyMetadatas` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPositions` (r:1 w:0) + /// Proof: `VeMinting::UserPositions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Locked` (r:1 w:0) + /// Proof: `VeMinting::Locked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::IncentiveConfigs` (r:1 w:1) + /// Proof: `VeMinting::IncentiveConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Epoch` (r:1 w:1) + /// Proof: `VeMinting::Epoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::PointHistory` (r:1 w:105) + /// Proof: `VeMinting::PointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::SlopeChanges` (r:104 w:0) + /// Proof: `VeMinting::SlopeChanges` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointEpoch` (r:1 w:1) + /// Proof: `VeMinting::UserPointEpoch` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointHistory` (r:1 w:1) + /// Proof: `VeMinting::UserPointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Rewards` (r:1 w:1) + /// Proof: `VeMinting::Rewards` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserRewardPerTokenPaid` (r:1 w:1) + /// Proof: `VeMinting::UserRewardPerTokenPaid` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Supply` (r:1 w:0) + /// Proof: `VeMinting::Supply` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn deposit_markup() -> Weight { + // Proof Size summary in bytes: + // Measured: `2260` + // Estimated: `260650` + // Minimum execution time: 820_796_000 picoseconds. + Weight::from_parts(831_306_000, 0) + .saturating_add(Weight::from_parts(0, 260650)) + .saturating_add(T::DbWeight::get().reads(125)) + .saturating_add(T::DbWeight::get().writes(118)) + } + /// Storage: `VeMinting::MarkupCoefficient` (r:1 w:0) + /// Proof: `VeMinting::MarkupCoefficient` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserMarkupInfos` (r:1 w:1) + /// Proof: `VeMinting::UserMarkupInfos` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::LockedTokens` (r:1 w:1) + /// Proof: `VeMinting::LockedTokens` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::TotalLock` (r:1 w:1) + /// Proof: `VeMinting::TotalLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tokens::Locks` (r:1 w:1) + /// Proof: `Tokens::Locks` (`max_values`: None, `max_size`: Some(1271), added: 3746, mode: `MaxEncodedLen`) + /// Storage: `Tokens::Accounts` (r:1 w:1) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(118), added: 2593, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::CurrencyMetadatas` (r:1 w:0) + /// Proof: `AssetRegistry::CurrencyMetadatas` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPositions` (r:1 w:0) + /// Proof: `VeMinting::UserPositions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Locked` (r:1 w:0) + /// Proof: `VeMinting::Locked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::IncentiveConfigs` (r:1 w:1) + /// Proof: `VeMinting::IncentiveConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Epoch` (r:1 w:1) + /// Proof: `VeMinting::Epoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::PointHistory` (r:1 w:105) + /// Proof: `VeMinting::PointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::SlopeChanges` (r:104 w:0) + /// Proof: `VeMinting::SlopeChanges` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointEpoch` (r:1 w:1) + /// Proof: `VeMinting::UserPointEpoch` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointHistory` (r:1 w:1) + /// Proof: `VeMinting::UserPointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Rewards` (r:1 w:1) + /// Proof: `VeMinting::Rewards` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserRewardPerTokenPaid` (r:1 w:1) + /// Proof: `VeMinting::UserRewardPerTokenPaid` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Supply` (r:1 w:0) + /// Proof: `VeMinting::Supply` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn withdraw_markup() -> Weight { + // Proof Size summary in bytes: + // Measured: `2725` + // Estimated: `261115` + // Minimum execution time: 813_232_000 picoseconds. + Weight::from_parts(823_401_000, 0) + .saturating_add(Weight::from_parts(0, 261115)) + .saturating_add(T::DbWeight::get().reads(125)) + .saturating_add(T::DbWeight::get().writes(118)) + } + /// Storage: `VeMinting::Locked` (r:1 w:1) + /// Proof: `VeMinting::Locked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VeMinting::Supply` (r:1 w:1) + /// Proof: `VeMinting::Supply` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPositions` (r:1 w:1) + /// Proof: `VeMinting::UserPositions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserLocked` (r:1 w:1) + /// Proof: `VeMinting::UserLocked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tokens::Accounts` (r:2 w:2) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(118), added: 2593, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::CurrencyMetadatas` (r:1 w:0) + /// Proof: `AssetRegistry::CurrencyMetadatas` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::IncentiveConfigs` (r:1 w:1) + /// Proof: `VeMinting::IncentiveConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Epoch` (r:1 w:1) + /// Proof: `VeMinting::Epoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::PointHistory` (r:1 w:105) + /// Proof: `VeMinting::PointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::SlopeChanges` (r:104 w:0) + /// Proof: `VeMinting::SlopeChanges` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Rewards` (r:1 w:1) + /// Proof: `VeMinting::Rewards` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserRewardPerTokenPaid` (r:1 w:1) + /// Proof: `VeMinting::UserRewardPerTokenPaid` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointHistory` (r:0 w:1) + /// Proof: `VeMinting::UserPointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointEpoch` (r:0 w:1) + /// Proof: `VeMinting::UserPointEpoch` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn redeem_unlock() -> Weight { + // Proof Size summary in bytes: + // Measured: `2525` + // Estimated: `260915` + // Minimum execution time: 818_341_000 picoseconds. + Weight::from_parts(827_248_000, 0) + .saturating_add(Weight::from_parts(0, 260915)) + .saturating_add(T::DbWeight::get().reads(122)) + .saturating_add(T::DbWeight::get().writes(120)) + } + /// Storage: `VeMinting::MarkupCoefficient` (r:1 w:0) + /// Proof: `VeMinting::MarkupCoefficient` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VeMinting::LockedTokens` (r:2 w:1) + /// Proof: `VeMinting::LockedTokens` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserMarkupInfos` (r:1 w:1) + /// Proof: `VeMinting::UserMarkupInfos` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPositions` (r:1 w:0) + /// Proof: `VeMinting::UserPositions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Locked` (r:1 w:0) + /// Proof: `VeMinting::Locked` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::IncentiveConfigs` (r:1 w:1) + /// Proof: `VeMinting::IncentiveConfigs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Epoch` (r:1 w:1) + /// Proof: `VeMinting::Epoch` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::PointHistory` (r:1 w:1) + /// Proof: `VeMinting::PointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointEpoch` (r:1 w:1) + /// Proof: `VeMinting::UserPointEpoch` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserPointHistory` (r:1 w:1) + /// Proof: `VeMinting::UserPointHistory` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Rewards` (r:1 w:1) + /// Proof: `VeMinting::Rewards` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::UserRewardPerTokenPaid` (r:1 w:1) + /// Proof: `VeMinting::UserRewardPerTokenPaid` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::SlopeChanges` (r:1 w:1) + /// Proof: `VeMinting::SlopeChanges` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `VeMinting::Supply` (r:1 w:0) + /// Proof: `VeMinting::Supply` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn refresh() -> Weight { + // Proof Size summary in bytes: + // Measured: `1687` + // Estimated: `7627` + // Minimum execution time: 129_204_000 picoseconds. + Weight::from_parts(130_316_000, 0) + .saturating_add(Weight::from_parts(0, 7627)) + .saturating_add(T::DbWeight::get().reads(16)) + .saturating_add(T::DbWeight::get().writes(10)) + } } diff --git a/runtime/bifrost-polkadot/src/weights/bifrost_vtoken_minting.rs b/runtime/bifrost-polkadot/src/weights/bifrost_vtoken_minting.rs index 01106010bc..c53ec8bb6b 100644 --- a/runtime/bifrost-polkadot/src/weights/bifrost_vtoken_minting.rs +++ b/runtime/bifrost-polkadot/src/weights/bifrost_vtoken_minting.rs @@ -299,4 +299,28 @@ impl bifrost_vtoken_minting::WeightInfo for BifrostWeig Weight::from_parts(16_763_000, 3492) .saturating_add(T::DbWeight::get().reads(1)) } + + fn mint_with_lock() -> Weight { + Weight::from_parts(325_143_000, 8769) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + + fn unlock_incentive_minted_vtoken() -> Weight { + Weight::from_parts(227_544_000, 8769) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + + fn set_incentive_coef() -> Weight { + Weight::from_parts(44_736_000, 1493) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + + fn set_vtoken_incentive_lock_blocks() -> Weight { + Weight::from_parts(44_736_000, 1493) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } }