From 68949048e93ed048f20120f85ca59bd32d5ecf4b Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios <54085674+JuaniRios@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:17:19 +0100 Subject: [PATCH] Feature/plmc 242 implement correct duration of vesting (#70) * wip * feat(244): linear increase of duration based on vesting * Update pallets/funding/src/types.rs Co-authored-by: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> * feat(242): small changes from Leonardo's feedback * feat(242): change week to day conversion on config type * chore(242): fmt * feat(242): small optimization --------- Co-authored-by: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> --- pallets/funding/src/functions.rs | 30 ++- pallets/funding/src/lib.rs | 10 +- pallets/funding/src/mock.rs | 4 +- pallets/funding/src/tests.rs | 338 +++++++++++++++++++------ pallets/funding/src/traits.rs | 8 +- pallets/funding/src/types.rs | 82 +++++- pallets/parachain-staking/src/tests.rs | 252 +----------------- runtimes/standalone/src/lib.rs | 6 +- runtimes/testnet/src/lib.rs | 6 +- 9 files changed, 377 insertions(+), 359 deletions(-) diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index 5615c5b78..1242985c7 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -21,7 +21,7 @@ use super::*; use sp_std::marker::PhantomData; -use crate::traits::{BondingRequirementCalculation, ProvideStatemintPrice}; +use crate::traits::{BondingRequirementCalculation, ProvideStatemintPrice, VestingDurationCalculation}; use frame_support::{ dispatch::DispatchResult, ensure, @@ -38,6 +38,7 @@ use sp_arithmetic::Perquintill; use polimec_traits::ReleaseSchedule; use sp_arithmetic::traits::{CheckedDiv, CheckedSub, Zero}; +use sp_runtime::traits::Convert; use sp_std::prelude::*; // Round transition functions @@ -1639,7 +1640,7 @@ impl Pallet { multiplier: MultiplierOf, plmc_price: PriceOf, ) -> Result, DispatchError> { - let usd_bond = multiplier.calculate_bonding_requirement(ticket_size).map_err(|_| Error::::BadMath)?; + let usd_bond = multiplier.calculate_bonding_requirement::(ticket_size).map_err(|_| Error::::BadMath)?; plmc_price.reciprocal().ok_or(Error::::BadMath)?.checked_mul_int(usd_bond).ok_or(Error::::BadMath.into()) } @@ -1647,18 +1648,19 @@ impl Pallet { /// calculate the amount and vesting periods of bonded PLMC and reward CT tokens. pub fn calculate_vesting_info( _caller: AccountIdOf, - _multiplier: MultiplierOf, + multiplier: MultiplierOf, bonded_amount: BalanceOf, ) -> Result>, DispatchError> { // TODO: duration should depend on `_multiplier` and `_caller` credential - let duration: u32 = 1u32 * parachains_common::DAYS; - let amount_per_block = bonded_amount.checked_div(&duration.into()).ok_or(Error::::BadMath)?; + let duration: T::BlockNumber = multiplier.calculate_vesting_duration::(); + let duration_as_balance = T::BlockNumberToBalance::convert(duration); + let amount_per_block = if duration_as_balance == Zero::zero() { + bonded_amount + } else { + bonded_amount.checked_div(&duration_as_balance).ok_or(Error::::BadMath)? + }; - Ok(VestingInfo { - total_amount: bonded_amount, - amount_per_block, - duration: >::from(duration), - }) + Ok(VestingInfo { total_amount: bonded_amount, amount_per_block, duration }) } /// Calculates the price (in USD) of contribution tokens for the Community and Remainder Rounds @@ -1780,7 +1782,7 @@ impl Pallet { let usd_bond_needed = bid .multiplier - .calculate_bonding_requirement(ticket_size) + .calculate_bonding_requirement::(ticket_size) .map_err(|_| Error::::BadMath)?; let plmc_bond_needed = plmc_price .reciprocal() @@ -1898,8 +1900,10 @@ impl Pallet { bid.funding_asset_amount_locked = funding_asset_amount_needed; - let usd_bond_needed = - bid.multiplier.calculate_bonding_requirement(new_ticket_size).map_err(|_| Error::::BadMath)?; + let usd_bond_needed = bid + .multiplier + .calculate_bonding_requirement::(new_ticket_size) + .map_err(|_| Error::::BadMath)?; let plmc_bond_needed = plmc_price .reciprocal() .ok_or(Error::::BadMath)? diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 7821592ef..eaf61371f 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -259,11 +259,12 @@ const PLMC_STATEMINT_ID: u32 = 2069; #[frame_support::pallet(dev_mode)] pub mod pallet { use super::*; - use crate::traits::{BondingRequirementCalculation, ProvideStatemintPrice}; + use crate::traits::{BondingRequirementCalculation, ProvideStatemintPrice, VestingDurationCalculation}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use local_macros::*; use sp_arithmetic::Percent; + use sp_runtime::traits::Convert; #[pallet::pallet] pub struct Pallet(_); @@ -277,7 +278,7 @@ pub mod pallet { // TODO: PLMC-153 + MaybeSerializeDeserialize: Maybe needed for JSON serialization @ Genesis: https://github.com/paritytech/substrate/issues/12738#issuecomment-1320921201 /// Multiplier that decides how much PLMC needs to be bonded for a token buy/bid - type Multiplier: Parameter + BondingRequirementCalculation + Default + From + Copy; + type Multiplier: Parameter + BondingRequirementCalculation + VestingDurationCalculation + Default + Copy; /// The inner balance type we will use for all of our outer currency types. (e.g native, funding, CTs) type Balance: Balance + From + FixedPointOperand; @@ -395,6 +396,11 @@ pub mod pallet { type EvaluatorSlash: Get; type TreasuryAccount: Get>; + + /// Convert 24 hours as FixedU128, to the corresponding amount of blocks in the same type as frame_system + type DaysToBlocks: Convert>; + + type BlockNumberToBalance: Convert, BalanceOf>; } #[pallet::storage] diff --git a/pallets/funding/src/mock.rs b/pallets/funding/src/mock.rs index 1ebde34b4..5d799810b 100644 --- a/pallets/funding/src/mock.rs +++ b/pallets/funding/src/mock.rs @@ -244,10 +244,12 @@ impl pallet_funding::Config for TestRuntime { type Balance = Balance; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); + type BlockNumberToBalance = ConvertInto; type CandleAuctionDuration = CandleAuctionDuration; type CommunityFundingDuration = CommunityRoundDuration; type ContributionTokenCurrency = LocalAssets; type ContributionVesting = ConstU32<4>; + type DaysToBlocks = DaysToBlocks; type EnglishAuctionDuration = EnglishAuctionDuration; type EvaluationDuration = EvaluationDuration; type EvaluationSuccessThreshold = EarlyEvaluationThreshold; @@ -260,7 +262,7 @@ impl pallet_funding::Config for TestRuntime { type MaxContributionsPerUser = ConstU32<4>; type MaxEvaluationsPerUser = ConstU32<4>; type MaxProjectsToUpdatePerBlock = ConstU32<100>; - type Multiplier = Multiplier; + type Multiplier = Multiplier; type NativeCurrency = Balances; type PalletId = FundingPalletId; type PreImageLimit = ConstU32<1024>; diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index f32f0b4d0..cb8af6212 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -17,18 +17,9 @@ // If you feel like getting in touch with us, you ca ,n do so at info@polimec.org //! Tests for Funding pallet. -use super::*; -use crate as pallet_funding; -use crate::{ - mock::{FundingModule, *}, - tests::testing_macros::{assert_close_enough, call_and_is_ok, extract_from_event}, - traits::{BondingRequirementCalculation, ProvideStatemintPrice}, - CurrencyMetadata, Error, ParticipantsSize, ProjectMetadata, TicketSize, - UpdateType::{CommunityFundingStart, RemainderFundingStart}, -}; -use assert_matches2::assert_matches; +use std::{cell::RefCell, cmp::min, collections::BTreeMap, iter::zip, ops::Div}; -use defaults::*; +use assert_matches2::assert_matches; use frame_support::{ assert_noop, assert_ok, traits::{ @@ -42,14 +33,26 @@ use frame_support::{ }, weights::Weight, }; -use helper_functions::*; use parachains_common::DAYS; -use polimec_traits::ReleaseSchedule; use sp_arithmetic::{traits::Zero, Percent, Perquintill}; use sp_core::H256; use sp_runtime::{DispatchError, Either}; use sp_std::marker::PhantomData; -use std::{cell::RefCell, collections::BTreeMap, iter::zip, ops::Div}; + +use defaults::*; +use helper_functions::*; +use polimec_traits::ReleaseSchedule; + +use crate as pallet_funding; +use crate::{ + mock::{FundingModule, *}, + tests::testing_macros::{assert_close_enough, call_and_is_ok, extract_from_event}, + traits::{BondingRequirementCalculation, ProvideStatemintPrice, VestingDurationCalculation}, + CurrencyMetadata, Error, ParticipantsSize, ProjectMetadata, TicketSize, + UpdateType::{CommunityFundingStart, RemainderFundingStart}, +}; + +use super::*; type ProjectIdOf = ::ProjectIdentifier; type UserToPLMCBalance = Vec<(AccountId, BalanceOf)>; @@ -214,6 +217,9 @@ const BIDDER_2: AccountId = 31; const BIDDER_3: AccountId = 32; const BIDDER_4: AccountId = 33; const BIDDER_5: AccountId = 34; +const BIDDER_6: AccountId = 35; +const BIDDER_7: AccountId = 36; +const BIDDER_8: AccountId = 37; const BUYER_1: AccountId = 40; const BUYER_2: AccountId = 41; const BUYER_3: AccountId = 42; @@ -1289,7 +1295,8 @@ pub mod helper_functions { let mut output = UserToPLMCBalance::new(); for bid in bids { let usd_ticket_size = bid.price.saturating_mul_int(bid.amount); - let usd_bond = bid.multiplier.unwrap_or_default().calculate_bonding_requirement(usd_ticket_size).unwrap(); + let multiplier = bid.multiplier.unwrap_or_default(); + let usd_bond = multiplier.calculate_bonding_requirement::(usd_ticket_size).unwrap(); let plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(usd_bond); output.push((bid.bidder, plmc_bond)); } @@ -1307,7 +1314,11 @@ pub mod helper_functions { let final_price = if bid.price < price { bid.price } else { price }; let usd_ticket_size = final_price.saturating_mul_int(bid.amount); - let usd_bond = bid.multiplier.unwrap_or_default().calculate_bonding_requirement(usd_ticket_size).unwrap(); + let usd_bond = bid + .multiplier + .unwrap_or_default() + .calculate_bonding_requirement::(usd_ticket_size) + .unwrap(); let plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(usd_bond); output.push((bid.bidder, plmc_bond)); } @@ -1333,7 +1344,11 @@ pub mod helper_functions { let mut output = UserToPLMCBalance::new(); for cont in contributions { let usd_ticket_size = token_usd_price.saturating_mul_int(cont.amount); - let usd_bond = cont.multiplier.unwrap_or_default().calculate_bonding_requirement(usd_ticket_size).unwrap(); + let usd_bond = cont + .multiplier + .unwrap_or_default() + .calculate_bonding_requirement::(usd_ticket_size) + .unwrap(); let plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(usd_bond); output.push((cont.contributor, plmc_bond)); } @@ -1712,18 +1727,6 @@ mod creation_round_success { mod creation_round_failure { use super::*; - #[test] - #[ignore] - fn only_with_credential_can_create() { - new_test_ext().execute_with(|| { - let project_metadata = default_project(0); - assert_noop!( - FundingModule::create(RuntimeOrigin::signed(ISSUER), project_metadata), - Error::::NotAuthorized - ); - }) - } - #[test] fn price_too_low() { let wrong_project: ProjectMetadataOf = ProjectMetadata { @@ -1773,21 +1776,6 @@ mod creation_round_failure { let project_err = test_env.create_project(ISSUER, wrong_project).unwrap_err(); assert_eq!(project_err, Error::::TicketSizeError.into()); } - - #[test] - #[ignore = "ATM only the first error will be thrown"] - fn multiple_field_error() { - let wrong_project: ProjectMetadataOf = ProjectMetadata { - minimum_price: 0_u128.into(), - ticket_size: TicketSize { minimum: None, maximum: None }, - participants_size: ParticipantsSize { minimum: None, maximum: None }, - ..Default::default() - }; - let test_env = TestEnvironment::new(); - test_env.mint_plmc_to(default_plmc_balances()); - let project_err = test_env.create_project(ISSUER, wrong_project).unwrap_err(); - assert_eq!(project_err, Error::::TicketSizeError.into()); - } } mod evaluation_round_success { @@ -2837,7 +2825,30 @@ mod auction_round_success { let issuer = ISSUER; let project = default_project(test_env.get_new_nonce()); let evaluations = default_evaluations(); - let bids = default_bids(); + let bids = vec![ + TestBid::new(BIDDER_1, 40_000 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT), + TestBid::new( + BIDDER_2, + 152_000 * ASSET_UNIT, + 11.into(), + Some(10u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_3, + 20_000 * ASSET_UNIT, + 17.into(), + Some(2u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_4, + 88_000 * ASSET_UNIT, + 18.into(), + Some(25u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + ]; let community_contributions = default_community_buys(); let remainder_contributions = vec![]; @@ -2862,8 +2873,9 @@ mod auction_round_success { let vesting_info = bid.plmc_vesting_info.unwrap(); let now = test_env.current_block(); - let blocks_passed = now - vest_start_block; - let vested_amount = vesting_info.amount_per_block * blocks_passed as u128; + + let blocks_vested = min(vesting_info.duration, now - vest_start_block); + let vested_amount = vesting_info.amount_per_block * blocks_vested as u128; let prev_free_balance = test_env.in_ext(|| ::NativeCurrency::balance(&bid.bidder)); @@ -3219,7 +3231,7 @@ mod auction_round_failure { let test_env = TestEnvironment::new(); let auctioning_project = AuctioningProject::new_with(&test_env, default_project(0), ISSUER, default_evaluations()); - let mul_2 = MultiplierOf::::from(2u32); + let mul_2 = MultiplierOf::::new(2u8).unwrap(); let bids = vec![ TestBid::new(BIDDER_1, 10_000, 2_u128.into(), None, AcceptedFundingAsset::USDC), TestBid::new(BIDDER_2, 13_000, 3_u128.into(), Some(mul_2), AcceptedFundingAsset::USDC), @@ -3705,7 +3717,7 @@ mod community_round_success { let token_price = project_details.weighted_average_price.unwrap(); // Create a contribution vector that will reach the limit of contributions for a user-project - let multiplier: Option> = Some(MultiplierOf::::from(3u32)); + let multiplier: Option> = Some(MultiplierOf::::new(3u8).unwrap()); let token_amount: BalanceOf = 10 * ASSET_UNIT; let range = 0..::MaxContributionsPerUser::get(); let contributions: TestContributions = range @@ -4348,7 +4360,30 @@ mod community_round_success { let issuer = ISSUER; let project = default_project(test_env.get_new_nonce()); let evaluations = default_evaluations(); - let bids = default_bids(); + let bids = vec![ + TestBid::new(BIDDER_1, 40_000 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT), + TestBid::new( + BIDDER_2, + 152_000 * ASSET_UNIT, + 11.into(), + Some(10u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_3, + 20_000 * ASSET_UNIT, + 17.into(), + Some(2u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_4, + 88_000 * ASSET_UNIT, + 18.into(), + Some(25u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + ]; let community_contributions = default_community_buys(); let remainder_contributions = vec![]; @@ -4375,8 +4410,8 @@ mod community_round_success { let vesting_info = contribution.plmc_vesting_info.unwrap(); let now = test_env.current_block(); - let blocks_passed = now - vest_start_block; - let vested_amount = vesting_info.amount_per_block * blocks_passed as u128; + let blocks_vested = min(vesting_info.duration, now - vest_start_block); + let vested_amount = vesting_info.amount_per_block * blocks_vested as u128; let prev_free_balance = test_env.in_ext(|| ::NativeCurrency::balance(&contribution.contributor)); @@ -5415,15 +5450,24 @@ mod remainder_round_success { }); let now = test_env.current_block(); - let blocks_passed = now - vest_start_block; let bid_plmc_balances = stored_bids .into_iter() - .map(|b| (b.bidder, b.plmc_vesting_info.unwrap().amount_per_block * blocks_passed as u128)) + .map(|b| { + (b.bidder, { + let blocks_vested = min(b.plmc_vesting_info.unwrap().duration, now - vest_start_block); + b.plmc_vesting_info.unwrap().amount_per_block * blocks_vested as u128 + }) + }) .collect::>(); let contributed_plmc_balances = stored_contributions .into_iter() - .map(|c| (c.contributor, c.plmc_vesting_info.unwrap().amount_per_block * blocks_passed as u128)) + .map(|c| { + (c.contributor, { + let blocks_vested = min(c.plmc_vesting_info.unwrap().duration, now - vest_start_block); + c.plmc_vesting_info.unwrap().amount_per_block * blocks_vested as u128 + }) + }) .collect::>(); let merged_plmc_balances = generic_map_merge_reduce( @@ -6151,6 +6195,115 @@ mod funding_end { assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); } + + #[test] + fn multiplier_gets_correct_vesting_duration() { + let test_env = TestEnvironment::new(); + let issuer = ISSUER; + let project = default_project(test_env.get_new_nonce()); + let evaluations = default_evaluations(); + let bids = vec![ + TestBid::new(BIDDER_1, 40_000 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT), + TestBid::new( + BIDDER_2, + 40_000 * ASSET_UNIT, + 15.into(), + Some(1u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_3, + 152_000 * ASSET_UNIT, + 11.into(), + Some(2u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_4, + 20_000 * ASSET_UNIT, + 17.into(), + Some(3u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_5, + 9_000 * ASSET_UNIT, + 18.into(), + Some(19u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_6, + 1_000 * ASSET_UNIT, + 18.into(), + Some(20u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_7, + 8_000 * ASSET_UNIT, + 18.into(), + Some(24u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_8, + 68_000 * ASSET_UNIT, + 18.into(), + Some(25u8.try_into().unwrap()), + AcceptedFundingAsset::USDT, + ), + ]; + let community_contributions = default_community_buys(); + let remainder_contributions = vec![]; + + let finished_project = FinishedProject::new_with( + &test_env, + project, + issuer, + evaluations, + bids, + community_contributions, + remainder_contributions, + ); + test_env.advance_time(::SuccessToSettlementTime::get()).unwrap(); + + test_env.advance_time(10u64).unwrap(); + let details = finished_project.get_project_details(); + assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); + + let mut stored_bids = test_env + .in_ext(|| Bids::::iter_prefix_values((finished_project.project_id,)).collect::>()); + + stored_bids.sort_by_key(|bid| bid.bidder); + let one_week_in_blocks = DAYS * 7; + assert_eq!(stored_bids[0].plmc_vesting_info.unwrap().duration, 1u64); + assert_eq!(stored_bids[1].plmc_vesting_info.unwrap().duration, 1u64); + assert_eq!( + stored_bids[2].plmc_vesting_info.unwrap().duration, + FixedU128::from_rational(2167, 1000).saturating_mul_int(one_week_in_blocks as u64) + ); + assert_eq!( + stored_bids[3].plmc_vesting_info.unwrap().duration, + FixedU128::from_rational(4334, 1000).saturating_mul_int(one_week_in_blocks as u64) + ); + assert_eq!( + stored_bids[4].plmc_vesting_info.unwrap().duration, + FixedU128::from_rational(39006, 1000).saturating_mul_int(one_week_in_blocks as u64) + ); + assert_eq!( + stored_bids[5].plmc_vesting_info.unwrap().duration, + FixedU128::from_rational(41173, 1000).saturating_mul_int(one_week_in_blocks as u64) + ); + assert_eq!( + stored_bids[6].plmc_vesting_info.unwrap().duration, + FixedU128::from_rational(49841, 1000).saturating_mul_int(one_week_in_blocks as u64) + ); + assert_eq!( + stored_bids[7].plmc_vesting_info.unwrap().duration, + FixedU128::from_rational(52008, 1000).saturating_mul_int(one_week_in_blocks as u64) + ); + } } mod test_helper_functions { @@ -6210,35 +6363,35 @@ mod test_helper_functions { const BIDDER_1: AccountIdOf = 1u64; const TOKEN_AMOUNT_1: u128 = 120_0_000_000_000_u128; const PRICE_PER_TOKEN_1: f64 = 0.3f64; - const MULTIPLIER_1: u32 = 1u32; + const MULTIPLIER_1: u8 = 1u8; const _TICKET_SIZE_USD_1: u128 = 36_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_1: u128 = 4_2_857_142_857_u128; const BIDDER_2: AccountIdOf = 2u64; const TOKEN_AMOUNT_2: u128 = 5023_0_000_000_000_u128; const PRICE_PER_TOKEN_2: f64 = 13f64; - const MULTIPLIER_2: u32 = 2u32; + const MULTIPLIER_2: u8 = 2u8; const _TICKET_SIZE_USD_2: u128 = 65_299_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_2: u128 = 3_886_8_452_380_952_u128; const BIDDER_3: AccountIdOf = 3u64; const TOKEN_AMOUNT_3: u128 = 20_000_0_000_000_000_u128; const PRICE_PER_TOKEN_3: f64 = 20f64; - const MULTIPLIER_3: u32 = 17u32; + const MULTIPLIER_3: u8 = 17u8; const _TICKET_SIZE_USD_3: u128 = 400_000_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_3: u128 = 2_801_1_204_481_792_u128; const BIDDER_4: AccountIdOf = 4u64; const TOKEN_AMOUNT_4: u128 = 1_000_000_0_000_000_000_u128; const PRICE_PER_TOKEN_4: f64 = 5.52f64; - const MULTIPLIER_4: u32 = 25u32; + const MULTIPLIER_4: u8 = 25u8; const _TICKET_SIZE_USD_4: u128 = 5_520_000_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_4: u128 = 26_285_7_142_857_142_u128; const BIDDER_5: AccountIdOf = 5u64; const TOKEN_AMOUNT_5: u128 = 0_1_233_000_000_u128; const PRICE_PER_TOKEN_5: f64 = 11.34f64; - const MULTIPLIER_5: u32 = 10u32; + const MULTIPLIER_5: u8 = 10u8; const _TICKET_SIZE_USD_5: u128 = 1_3_982_220_000_u128; // TODO: Is this due to rounding errors? // Should be in reality 0.0166455, but we get 0.0166454999. i.e error of 0.0000000001 PLMC @@ -6256,35 +6409,35 @@ mod test_helper_functions { BIDDER_1, TOKEN_AMOUNT_1, PriceOf::::from_float(PRICE_PER_TOKEN_1), - Some(MultiplierOf::::from(MULTIPLIER_1)), + Some(MultiplierOf::::new(MULTIPLIER_1).unwrap()), AcceptedFundingAsset::USDT, ), TestBid::new( BIDDER_2, TOKEN_AMOUNT_2, PriceOf::::from_float(PRICE_PER_TOKEN_2), - Some(MultiplierOf::::from(MULTIPLIER_2)), + Some(MultiplierOf::::new(MULTIPLIER_2).unwrap()), AcceptedFundingAsset::USDT, ), TestBid::new( BIDDER_3, TOKEN_AMOUNT_3, PriceOf::::from_float(PRICE_PER_TOKEN_3), - Some(MultiplierOf::::from(MULTIPLIER_3)), + Some(MultiplierOf::::new(MULTIPLIER_3).unwrap()), AcceptedFundingAsset::USDT, ), TestBid::new( BIDDER_4, TOKEN_AMOUNT_4, PriceOf::::from_float(PRICE_PER_TOKEN_4), - Some(MultiplierOf::::from(MULTIPLIER_4)), + Some(MultiplierOf::::new(MULTIPLIER_4).unwrap()), AcceptedFundingAsset::USDT, ), TestBid::new( BIDDER_5, TOKEN_AMOUNT_5, PriceOf::::from_float(PRICE_PER_TOKEN_5), - Some(MultiplierOf::::from(MULTIPLIER_5)), + Some(MultiplierOf::::new(MULTIPLIER_5).unwrap()), AcceptedFundingAsset::USDT, ), ]; @@ -6308,31 +6461,31 @@ mod test_helper_functions { const CONTRIBUTOR_1: AccountIdOf = 1u64; const TOKEN_AMOUNT_1: u128 = 120_0_000_000_000_u128; - const MULTIPLIER_1: u32 = 1u32; + const MULTIPLIER_1: u8 = 1u8; const _TICKET_SIZE_USD_1: u128 = 1_958_4_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_1: u128 = 233_1_428_571_428_u128; const CONTRIBUTOR_2: AccountIdOf = 2u64; const TOKEN_AMOUNT_2: u128 = 5023_0_000_000_000_u128; - const MULTIPLIER_2: u32 = 2u32; + const MULTIPLIER_2: u8 = 2u8; const _TICKET_SIZE_USD_2: u128 = 81_975_3_600_000_000_u128; const EXPECTED_PLMC_AMOUNT_2: u128 = 4_879_4_857_142_857_u128; const CONTRIBUTOR_3: AccountIdOf = 3u64; const TOKEN_AMOUNT_3: u128 = 20_000_0_000_000_000_u128; - const MULTIPLIER_3: u32 = 17u32; + const MULTIPLIER_3: u8 = 17u8; const _TICKET_SIZE_USD_3: u128 = 326_400_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_3: u128 = 2_285_7_142_857_142_u128; const CONTRIBUTOR_4: AccountIdOf = 4u64; const TOKEN_AMOUNT_4: u128 = 1_000_000_0_000_000_000_u128; - const MULTIPLIER_4: u32 = 25u32; + const MULTIPLIER_4: u8 = 25u8; const _TICKET_SIZE_4: u128 = 16_320_000_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_4: u128 = 77_714_2_857_142_857_u128; const CONTRIBUTOR_5: AccountIdOf = 5u64; const TOKEN_AMOUNT_5: u128 = 0_1_233_000_000_u128; - const MULTIPLIER_5: u32 = 10u32; + const MULTIPLIER_5: u8 = 10u8; const _TICKET_SIZE_5: u128 = 2_0_122_562_000_u128; const EXPECTED_PLMC_AMOUNT_5: u128 = 0_0_239_554_285_u128; @@ -6345,31 +6498,31 @@ mod test_helper_functions { TestContribution::new( CONTRIBUTOR_1, TOKEN_AMOUNT_1, - Some(MultiplierOf::::from(MULTIPLIER_1)), + Some(MultiplierOf::::new(MULTIPLIER_1).unwrap()), AcceptedFundingAsset::USDT, ), TestContribution::new( CONTRIBUTOR_2, TOKEN_AMOUNT_2, - Some(MultiplierOf::::from(MULTIPLIER_2)), + Some(MultiplierOf::::new(MULTIPLIER_2).unwrap()), AcceptedFundingAsset::USDT, ), TestContribution::new( CONTRIBUTOR_3, TOKEN_AMOUNT_3, - Some(MultiplierOf::::from(MULTIPLIER_3)), + Some(MultiplierOf::::new(MULTIPLIER_3).unwrap()), AcceptedFundingAsset::USDT, ), TestContribution::new( CONTRIBUTOR_4, TOKEN_AMOUNT_4, - Some(MultiplierOf::::from(MULTIPLIER_4)), + Some(MultiplierOf::::new(MULTIPLIER_4).unwrap()), AcceptedFundingAsset::USDT, ), TestContribution::new( CONTRIBUTOR_5, TOKEN_AMOUNT_5, - Some(MultiplierOf::::from(MULTIPLIER_5)), + Some(MultiplierOf::::new(MULTIPLIER_5).unwrap()), AcceptedFundingAsset::USDT, ), ]; @@ -6427,6 +6580,41 @@ mod misc_features { }); } + #[test] + fn calculate_vesting_duration() { + let default_multiplier = MultiplierOf::::default(); + let default_multiplier_duration = default_multiplier.calculate_vesting_duration::(); + assert_eq!(default_multiplier_duration, 1u64); + + let multiplier_1 = MultiplierOf::::new(1u8).unwrap(); + let multiplier_1_duration = multiplier_1.calculate_vesting_duration::(); + assert_eq!(multiplier_1_duration, 1u64); + + let multiplier_2 = MultiplierOf::::new(2u8).unwrap(); + let multiplier_2_duration = multiplier_2.calculate_vesting_duration::(); + assert_eq!(multiplier_2_duration, FixedU128::from_rational(2167, 1000).saturating_mul_int((DAYS * 7) as u64)); + + let multiplier_3 = MultiplierOf::::new(3u8).unwrap(); + let multiplier_3_duration = multiplier_3.calculate_vesting_duration::(); + assert_eq!(multiplier_3_duration, FixedU128::from_rational(4334, 1000).saturating_mul_int((DAYS * 7) as u64)); + + let multiplier_19 = MultiplierOf::::new(19u8).unwrap(); + let multiplier_19_duration = multiplier_19.calculate_vesting_duration::(); + assert_eq!(multiplier_19_duration, FixedU128::from_rational(39006, 1000).saturating_mul_int((DAYS * 7) as u64)); + + let multiplier_20 = MultiplierOf::::new(20u8).unwrap(); + let multiplier_20_duration = multiplier_20.calculate_vesting_duration::(); + assert_eq!(multiplier_20_duration, FixedU128::from_rational(41173, 1000).saturating_mul_int((DAYS * 7) as u64)); + + let multiplier_24 = MultiplierOf::::new(24u8).unwrap(); + let multiplier_24_duration = multiplier_24.calculate_vesting_duration::(); + assert_eq!(multiplier_24_duration, FixedU128::from_rational(49841, 1000).saturating_mul_int((DAYS * 7) as u64)); + + let multiplier_25 = MultiplierOf::::new(25u8).unwrap(); + let multiplier_25_duration = multiplier_25.calculate_vesting_duration::(); + assert_eq!(multiplier_25_duration, FixedU128::from_rational(52008, 1000).saturating_mul_int((DAYS * 7) as u64)); + } + #[test] fn sandbox() { assert!(true); diff --git a/pallets/funding/src/traits.rs b/pallets/funding/src/traits.rs index 58aaf838c..6bffe24a6 100644 --- a/pallets/funding/src/traits.rs +++ b/pallets/funding/src/traits.rs @@ -3,8 +3,12 @@ use frame_support::weights::Weight; use sp_arithmetic::FixedPointNumber; use sp_runtime::DispatchError; -pub trait BondingRequirementCalculation { - fn calculate_bonding_requirement(&self, ticket_size: BalanceOf) -> Result, ()>; +pub trait BondingRequirementCalculation { + fn calculate_bonding_requirement(&self, ticket_size: BalanceOf) -> Result, ()>; +} + +pub trait VestingDurationCalculation { + fn calculate_vesting_duration(&self) -> T::BlockNumber; } pub trait ProvideStatemintPrice { diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index 861021772..9998feb85 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -18,10 +18,6 @@ //! Types for Funding pallet. -use crate::{ - traits::{BondingRequirementCalculation, ProvideStatemintPrice}, - BalanceOf, -}; use frame_support::{pallet_prelude::*, traits::tokens::Balance as BalanceT}; use sp_arithmetic::{FixedPointNumber, FixedPointOperand}; use sp_runtime::traits::CheckedDiv; @@ -31,24 +27,70 @@ pub use config_types::*; pub use inner_types::*; pub use storage_types::*; +use crate::{ + traits::{BondingRequirementCalculation, ProvideStatemintPrice}, + BalanceOf, +}; + pub mod config_types { + use parachains_common::DAYS; + use sp_arithmetic::{traits::Saturating, FixedU128}; + use sp_runtime::traits::{Convert, One}; + + use crate::{traits::VestingDurationCalculation, Config}; + use super::*; #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, Copy, Ord, PartialOrd)] - pub struct Multiplier(pub T::Balance); - impl BondingRequirementCalculation for Multiplier { - fn calculate_bonding_requirement(&self, ticket_size: BalanceOf) -> Result, ()> { - ticket_size.checked_div(&self.0).ok_or(()) + pub struct Multiplier(u8); + + impl Multiplier { + /// Creates a new `Multiplier` if the value is between 1 and 25, otherwise returns an error. + pub fn new(x: u8) -> Result { + // The minimum and maximum values are chosen to be 1 and 25 respectively, as defined in the Knowledge Hub. + const MIN_VALID: u8 = 1; + const MAX_VALID: u8 = 25; + + if x >= MIN_VALID && x <= MAX_VALID { + Ok(Self(x)) + } else { + Err(()) + } + } + } + + impl BondingRequirementCalculation for Multiplier { + fn calculate_bonding_requirement(&self, ticket_size: BalanceOf) -> Result, ()> { + let balance_multiplier = BalanceOf::::from(self.0); + ticket_size.checked_div(&balance_multiplier).ok_or(()) } } - impl Default for Multiplier { + + impl VestingDurationCalculation for Multiplier { + fn calculate_vesting_duration(&self) -> T::BlockNumber { + // gradient "m" of the linear curve function y = m*x + b where x is the multiplier and y is the number of weeks + const GRADIENT: FixedU128 = FixedU128::from_rational(2167u128, 1000u128); + // negative constant (because we cannot have negative values, so we take the negative and do "-b" instead of "+b") "b" of the linear curve function y = m*x + b + const NEG_CONSTANT: FixedU128 = FixedU128::from_rational(2167u128, 1000u128); + + let multiplier_as_fixed = FixedU128::saturating_from_integer(self.0); + let weeks = GRADIENT.saturating_mul(multiplier_as_fixed).saturating_sub(NEG_CONSTANT); + + T::DaysToBlocks::convert(weeks * FixedU128::from_u32(7u32)).max(One::one()) + } + } + + impl Default for Multiplier { fn default() -> Self { - Self(1u32.into()) + Self(1u8) } } - impl From for Multiplier { - fn from(x: u32) -> Self { - Self(x.into()) + + impl TryFrom for Multiplier { + type Error = (); + + fn try_from(x: u8) -> Result { + Self::new(x) } } @@ -72,6 +114,20 @@ pub mod config_types { Mapping::get().get(&asset_id).cloned() } } + + pub struct DaysToBlocks; + impl Convert for DaysToBlocks { + fn convert(a: FixedU128) -> u64 { + let one_day_in_blocks = DAYS; + a.saturating_mul_int(one_day_in_blocks as u64) + } + } + impl Convert for DaysToBlocks { + fn convert(a: FixedU128) -> u32 { + let one_day_in_blocks = DAYS; + a.saturating_mul_int(one_day_in_blocks) + } + } } pub mod storage_types { diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index 113b28859..9c2d01419 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -22,6 +22,9 @@ //! 3. Public (Collator, Nominator) //! 4. Miscellaneous Property-Based Tests +use frame_support::{assert_noop, assert_ok}; +use sp_runtime::{traits::Zero, Perbill, Percent}; + use crate::{ assert_events_emitted, assert_events_emitted_match, assert_events_eq, assert_no_events, auto_compound::{AutoCompoundConfig, AutoCompoundDelegations}, @@ -33,8 +36,6 @@ use crate::{ AtStake, Bond, CollatorStatus, DelegationScheduledRequests, DelegatorAdded, DelegatorState, DelegatorStatus, Error, Event, Range, DELEGATOR_LOCK_ID, }; -use frame_support::{assert_noop, assert_ok}; -use sp_runtime::{traits::Zero, Perbill, Percent}; // ~~ ROOT ~~ @@ -2736,253 +2737,6 @@ fn delegator_schedule_revocation_total() { }); } -#[ignore] -#[test] -fn parachain_bond_inflation_reserve_matches_config() { - ExtBuilder::default() - .with_balances(vec![ - (1, 100), - (2, 100), - (3, 100), - (4, 100), - (5, 100), - (6, 100), - (7, 100), - (8, 100), - (9, 100), - (10, 100), - (11, 1), - ]) - .with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)]) - .with_delegations(vec![(6, 1, 10), (7, 1, 10), (8, 2, 10), (9, 2, 10), (10, 1, 10)]) - .build() - .execute_with(|| { - assert_eq!(Balances::free_balance(&11), 1); - // set parachain bond account so DefaultParachainBondReservePercent = 30% of inflation - // is allocated to this account hereafter - assert_ok!(ParachainStaking::set_parachain_bond_account(RuntimeOrigin::root(), 11)); - assert_events_eq!(Event::ParachainBondAccountSet { old: 0, new: 11 }); - roll_to_round_begin(2); - // chooses top TotalSelectedCandidates (5), in order - assert_events_eq!( - Event::CollatorChosen { round: 2, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 2, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 2, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 2, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 5, round: 2, selected_collators_number: 5, total_balance: 140 }, - ); - assert_eq!(Balances::free_balance(&11), 1); - // ~ set block author as 1 for all blocks this round - set_author(2, 1, 100); - roll_to_round_begin(4); - // distribute total issuance to collator 1 and its delegators 6, 7, 19 - assert_eq!(Balances::free_balance(&11), 16); - // ~ set block author as 1 for all blocks this round - set_author(3, 1, 100); - set_author(4, 1, 100); - set_author(5, 1, 100); - // 1. ensure delegators are paid for 2 rounds after they leave - assert_noop!( - ParachainStaking::schedule_leave_delegators(RuntimeOrigin::signed(66)), - Error::::DelegatorDNE - ); - assert_ok!(ParachainStaking::schedule_leave_delegators(RuntimeOrigin::signed(6))); - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 15 }, - Event::CollatorChosen { round: 4, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 4, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 4, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 4, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 4, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 15, round: 4, selected_collators_number: 5, total_balance: 140 }, - Event::DelegatorExitScheduled { round: 4, delegator: 6, scheduled_exit: 6 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 20 }, - Event::Rewarded { account: 6, rewards: 5 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, - ); - // fast forward to block in which delegator 6 exit executes - roll_to_round_begin(5); - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 16 }, - Event::CollatorChosen { round: 5, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 5, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 5, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 5, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 5, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 20, round: 5, selected_collators_number: 5, total_balance: 140 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 21 }, - Event::Rewarded { account: 6, rewards: 5 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, - ); - roll_to_round_begin(6); - assert_ok!(ParachainStaking::execute_leave_delegators(RuntimeOrigin::signed(6), 6, 10)); - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 16 }, - Event::CollatorChosen { round: 6, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 6, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 6, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 6, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 6, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 25, round: 6, selected_collators_number: 5, total_balance: 140 }, - Event::DelegatorLeftCandidate { - delegator: 6, - candidate: 1, - unstaked_amount: 10, - total_candidate_staked: 40, - }, - Event::DelegatorLeft { delegator: 6, unstaked_amount: 10 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 22 }, - Event::Rewarded { account: 6, rewards: 6 }, - Event::Rewarded { account: 7, rewards: 6 }, - Event::Rewarded { account: 10, rewards: 6 }, - ); - roll_to_round_begin(7); - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 17 }, - Event::CollatorChosen { round: 7, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 7, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 7, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 7, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 7, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 30, round: 7, selected_collators_number: 5, total_balance: 130 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 26 }, - Event::Rewarded { account: 7, rewards: 7 }, - Event::Rewarded { account: 10, rewards: 7 }, - ); - assert_eq!(Balances::free_balance(&11), 65); - roll_blocks(1); - assert_ok!(ParachainStaking::set_parachain_bond_reserve_percent( - RuntimeOrigin::root(), - Percent::from_percent(50) - )); - assert_events_eq!(Event::ParachainBondReservePercentSet { - old: Percent::from_percent(30), - new: Percent::from_percent(50), - }); - // 6 won't be paid for this round because they left already - set_author(6, 1, 100); - roll_to_round_begin(8); - // keep paying 6 - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 30 }, - Event::CollatorChosen { round: 8, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 8, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 8, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 8, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 8, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 35, round: 8, selected_collators_number: 5, total_balance: 130 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 21 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, - ); - assert_eq!(Balances::free_balance(&11), 95); - set_author(7, 1, 100); - roll_to_round_begin(9); - // no more paying 6 - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 32 }, - Event::CollatorChosen { round: 9, collator_account: 1, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 9, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 9, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 9, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 9, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 40, round: 9, selected_collators_number: 5, total_balance: 130 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 22 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, - ); - assert_eq!(Balances::free_balance(&11), 127); - set_author(8, 1, 100); - roll_blocks(1); - assert_ok!(ParachainStaking::delegate(RuntimeOrigin::signed(8), 1, 10, 10, 10)); - assert_events_eq!(Event::Delegation { - delegator: 8, - locked_amount: 10, - candidate: 1, - delegator_position: DelegatorAdded::AddedToTop { new_total: 50 }, - auto_compound: Percent::zero(), - }); - roll_to_round_begin(10); - // new delegation is not rewarded yet - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 33 }, - Event::CollatorChosen { round: 10, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 10, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 10, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 10, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 10, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 45, round: 10, selected_collators_number: 5, total_balance: 140 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 23 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, - ); - assert_eq!(Balances::free_balance(&11), 160); - set_author(9, 1, 100); - set_author(10, 1, 100); - roll_to_round_begin(11); - // new delegation is still not rewarded yet - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 35 }, - Event::CollatorChosen { round: 11, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 11, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 11, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 11, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 11, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 50, round: 11, selected_collators_number: 5, total_balance: 140 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 24 }, - Event::Rewarded { account: 7, rewards: 5 }, - Event::Rewarded { account: 10, rewards: 5 }, - ); - assert_eq!(Balances::free_balance(&11), 195); - roll_to_round_begin(12); - // new delegation is rewarded, 2 rounds after joining (`RewardPaymentDelay` is 2) - assert_events_eq!( - Event::ReservedForParachainBond { account: 11, value: 37 }, - Event::CollatorChosen { round: 12, collator_account: 1, total_exposed_amount: 50 }, - Event::CollatorChosen { round: 12, collator_account: 2, total_exposed_amount: 40 }, - Event::CollatorChosen { round: 12, collator_account: 3, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 12, collator_account: 4, total_exposed_amount: 20 }, - Event::CollatorChosen { round: 12, collator_account: 5, total_exposed_amount: 10 }, - Event::NewRound { starting_block: 55, round: 12, selected_collators_number: 5, total_balance: 140 }, - ); - roll_blocks(3); - assert_events_eq!( - Event::Rewarded { account: 1, rewards: 24 }, - Event::Rewarded { account: 7, rewards: 4 }, - Event::Rewarded { account: 10, rewards: 4 }, - Event::Rewarded { account: 8, rewards: 4 }, - ); - assert_eq!(Balances::free_balance(&11), 232); - }); -} - #[test] fn paid_collator_commission_matches_config() { ExtBuilder::default() diff --git a/runtimes/standalone/src/lib.rs b/runtimes/standalone/src/lib.rs index 3687db8d3..903c1d399 100644 --- a/runtimes/standalone/src/lib.rs +++ b/runtimes/standalone/src/lib.rs @@ -39,7 +39,7 @@ use frame_support::traits::AsEnsureOriginWithArg; use frame_system::EnsureSigned; pub use frame_system::{Call as SystemCall, EnsureRoot}; pub use pallet_balances::Call as BalancesCall; -use pallet_funding::BondTypeOf; +use pallet_funding::{BondTypeOf, DaysToBlocks}; use pallet_grandpa::AuthorityId as GrandpaId; pub use pallet_timestamp::Call as TimestampCall; use pallet_transaction_payment::{ConstFeeMultiplier, CurrencyAdapter, Multiplier}; @@ -360,10 +360,12 @@ impl pallet_funding::Config for Runtime { type Balance = Balance; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); + type BlockNumberToBalance = ConvertInto; type CandleAuctionDuration = CandleAuctionDuration; type CommunityFundingDuration = CommunityFundingDuration; type ContributionTokenCurrency = Assets; type ContributionVesting = ContributionVestingDuration; + type DaysToBlocks = DaysToBlocks; type EnglishAuctionDuration = EnglishAuctionDuration; type EvaluationDuration = EvaluationDuration; type EvaluationSuccessThreshold = EarlyEvaluationThreshold; @@ -375,7 +377,7 @@ impl pallet_funding::Config for Runtime { type MaxContributionsPerUser = ConstU32<256>; type MaxEvaluationsPerUser = (); type MaxProjectsToUpdatePerBlock = ConstU32<100>; - type Multiplier = pallet_funding::types::Multiplier; + type Multiplier = pallet_funding::types::Multiplier; type NativeCurrency = Balances; type PalletId = FundingPalletId; type PreImageLimit = ConstU32<1024>; diff --git a/runtimes/testnet/src/lib.rs b/runtimes/testnet/src/lib.rs index 6bc6ffb3e..0b73d880d 100644 --- a/runtimes/testnet/src/lib.rs +++ b/runtimes/testnet/src/lib.rs @@ -66,7 +66,7 @@ pub use crate::xcm_config::*; include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); // Polimec Shared Imports -use pallet_funding::BondTypeOf; +use pallet_funding::{BondTypeOf, DaysToBlocks}; pub use pallet_parachain_staking; pub use shared_configuration::*; @@ -487,10 +487,12 @@ impl pallet_funding::Config for Runtime { type Balance = Balance; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); + type BlockNumberToBalance = ConvertInto; type CandleAuctionDuration = CandleAuctionDuration; type CommunityFundingDuration = CommunityFundingDuration; type ContributionTokenCurrency = LocalAssets; type ContributionVesting = ContributionVestingDuration; + type DaysToBlocks = DaysToBlocks; type EnglishAuctionDuration = EnglishAuctionDuration; type EvaluationDuration = EvaluationDuration; type EvaluationSuccessThreshold = EarlyEvaluationThreshold; @@ -502,7 +504,7 @@ impl pallet_funding::Config for Runtime { type MaxContributionsPerUser = ConstU32<256>; type MaxEvaluationsPerUser = (); type MaxProjectsToUpdatePerBlock = ConstU32<100>; - type Multiplier = pallet_funding::types::Multiplier; + type Multiplier = pallet_funding::types::Multiplier; type NativeCurrency = Balances; type PalletId = FundingPalletId; type PreImageLimit = ConstU32<1024>;