From b046766d2e0f4805d2bb7f072915d6d6ea95a2d8 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Thu, 16 May 2024 12:26:13 +0200 Subject: [PATCH] optimize wap calculation --- pallets/funding/src/benchmarking.rs | 2 +- .../funding/src/functions/1_application.rs | 1 + pallets/funding/src/functions/3_auction.rs | 65 +++++++++++++- .../funding/src/functions/4_contribution.rs | 19 ++-- pallets/funding/src/functions/misc.rs | 88 +++++++++++++------ .../src/instantiator/async_features.rs | 69 +++++++-------- .../src/instantiator/chain_interactions.rs | 17 ++-- pallets/funding/src/lib.rs | 40 ++++++--- pallets/funding/src/tests/2_evaluation.rs | 1 + pallets/funding/src/tests/3_auction.rs | 8 +- pallets/funding/src/tests/4_community.rs | 3 +- pallets/funding/src/tests/misc.rs | 1 - pallets/funding/src/types.rs | 17 ++++ 13 files changed, 230 insertions(+), 101 deletions(-) diff --git a/pallets/funding/src/benchmarking.rs b/pallets/funding/src/benchmarking.rs index 739b7bd82..f7a45b637 100644 --- a/pallets/funding/src/benchmarking.rs +++ b/pallets/funding/src/benchmarking.rs @@ -1984,7 +1984,7 @@ mod benchmarks { #[block] { - Pallet::::do_auction_closing(project_id).unwrap(); + Pallet::::do_start_auction_closing(project_id).unwrap(); } // * validity checks * // Storage diff --git a/pallets/funding/src/functions/1_application.rs b/pallets/funding/src/functions/1_application.rs index 065f45a3b..683a6f09f 100644 --- a/pallets/funding/src/functions/1_application.rs +++ b/pallets/funding/src/functions/1_application.rs @@ -44,6 +44,7 @@ impl Pallet { total_bonded_plmc: Zero::zero(), evaluators_outcome: EvaluatorsOutcome::Unchanged, }, + auction_round_info: AuctionRoundInfoOf:: { is_oversubscribed: IsOversubscribed::No }, funding_end_block: None, parachain_id: None, migration_readiness_check: None, diff --git a/pallets/funding/src/functions/3_auction.rs b/pallets/funding/src/functions/3_auction.rs index dfce9a82f..e7f328aac 100644 --- a/pallets/funding/src/functions/3_auction.rs +++ b/pallets/funding/src/functions/3_auction.rs @@ -24,7 +24,7 @@ impl Pallet { /// Later on, `on_initialize` transitions the project into the closing auction round, by calling /// [`do_auction_closing`](Self::do_auction_closing). #[transactional] - pub fn do_auction_opening(caller: AccountIdOf, project_id: ProjectId) -> DispatchResultWithPostInfo { + pub fn do_start_auction_opening(caller: AccountIdOf, project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; let now = >::block_number(); @@ -109,7 +109,7 @@ impl Pallet { /// Later on, `on_initialize` ends the auction closing round and starts the community round, /// by calling [`do_community_funding`](Self::do_start_community_funding). #[transactional] - pub fn do_auction_closing(project_id: ProjectId) -> DispatchResultWithPostInfo { + pub fn do_start_auction_closing(project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; let now = >::block_number(); @@ -134,7 +134,7 @@ impl Pallet { // Schedule for automatic check by on_initialize. Success depending on enough funding reached let insertion_iterations = match Self::add_to_update_store( closing_end_block + 1u32.into(), - (&project_id, UpdateType::CommunityFundingStart), + (&project_id, UpdateType::AuctionClosingEnd), ) { Ok(iterations) => iterations, Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), @@ -149,6 +149,65 @@ impl Pallet { }) } + #[transactional] + pub fn do_end_auction_closing(project_id: ProjectId) -> DispatchResultWithPostInfo { + // * Get variables * + let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectMetadataNotFound)?; + let now = >::block_number(); + let auction_closing_start_block = + project_details.phase_transition_points.auction_closing.start().ok_or(Error::::TransitionPointNotSet)?; + let auction_closing_end_block = + project_details.phase_transition_points.auction_closing.end().ok_or(Error::::TransitionPointNotSet)?; + + // * Validity checks * + ensure!(now > auction_closing_end_block, Error::::TooEarlyForRound); + ensure!(project_details.status == ProjectStatus::AuctionClosing, Error::::IncorrectRound); + + // * Calculate new variables * + let end_block = Self::select_random_block(auction_closing_start_block, auction_closing_end_block); + + // * Update Storage * + let calculation_result = Self::decide_winning_bids( + project_id, + end_block, + project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size, + ); + + match calculation_result { + Err(e) => return Err(DispatchErrorWithPostInfo { post_info: ().into(), error: e }), + Ok((accepted_bids_count, rejected_bids_count)) => { + // Get info again after updating it with new price. + project_details.phase_transition_points.random_closing_ending = Some(end_block); + ProjectsDetails::::insert(project_id, project_details); + + let insertion_iterations = match Self::add_to_update_store( + now + 1u32.into(), + (&project_id, UpdateType::CommunityFundingStart), + ) { + Ok(iterations) => iterations, + Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), + }; + + // * Emit events * + Self::deposit_event(Event::::ProjectPhaseTransition { + project_id, + phase: ProjectPhases::CalculatingWAP, + }); + + Ok(PostDispatchInfo { + // TODO: make new benchmark + actual_weight: Some(WeightInfoOf::::start_community_funding( + insertion_iterations, + accepted_bids_count, + rejected_bids_count, + )), + pays_fee: Pays::Yes, + }) + }, + } + } + /// Bid for a project in the bidding stage. /// /// # Arguments diff --git a/pallets/funding/src/functions/4_contribution.rs b/pallets/funding/src/functions/4_contribution.rs index 44e8d20ce..967bb218f 100644 --- a/pallets/funding/src/functions/4_contribution.rs +++ b/pallets/funding/src/functions/4_contribution.rs @@ -42,16 +42,17 @@ impl Pallet { let end_block = Self::select_random_block(auction_closing_start_block, auction_closing_end_block); let community_start_block = now; let community_end_block = now.saturating_add(T::CommunityFundingDuration::get()).saturating_sub(One::one()); + let auction_allocation_size = + project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; + // * Update Storage * - let calculation_result = Self::calculate_weighted_average_price( - project_id, - end_block, - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size, - ); + let _ = Self::decide_winning_bids(project_id, end_block, auction_allocation_size)?; + let wap_result = Self::calculate_weighted_average_price(project_id); + let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - match calculation_result { + match wap_result { Err(e) => return Err(DispatchErrorWithPostInfo { post_info: ().into(), error: e }), - Ok((accepted_bids_count, rejected_bids_count)) => { + Ok(winning_bids_count) => { // Get info again after updating it with new price. project_details.phase_transition_points.random_closing_ending = Some(end_block); project_details @@ -75,10 +76,12 @@ impl Pallet { phase: ProjectPhases::CommunityFunding, }); + //TODO: address this + let rejected_bids_count = 0; Ok(PostDispatchInfo { actual_weight: Some(WeightInfoOf::::start_community_funding( insertion_iterations, - accepted_bids_count, + winning_bids_count, rejected_bids_count, )), pays_fee: Pays::Yes, diff --git a/pallets/funding/src/functions/misc.rs b/pallets/funding/src/functions/misc.rs index c1f42373d..d04a087d4 100644 --- a/pallets/funding/src/functions/misc.rs +++ b/pallets/funding/src/functions/misc.rs @@ -1,4 +1,5 @@ use super::*; +use itertools::Itertools; // Helper functions // ATTENTION: if this is called directly, it will not be transactional @@ -69,8 +70,7 @@ impl Pallet { Ok(VestingInfo { total_amount: bonded_amount, amount_per_block, duration }) } - /// Calculates the price (in USD) of contribution tokens for the Community and Remainder Rounds - pub fn calculate_weighted_average_price( + pub fn decide_winning_bids( project_id: ProjectId, end_block: BlockNumberFor, auction_allocation_size: BalanceOf, @@ -82,8 +82,6 @@ impl Pallet { // temp variable to store the total value of the bids (i.e price * amount = Cumulative Ticket Size) let mut bid_usd_value_sum = BalanceOf::::zero(); let project_account = Self::fund_account_id(project_id); - let plmc_price = T::PriceProvider::get_decimals_aware_price(PLMC_FOREIGN_ID, USD_DECIMALS, PLMC_DECIMALS) - .ok_or(Error::::PriceNotFound)?; let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectMetadataNotFound)?; let mut highest_accepted_price = project_metadata.minimum_price; @@ -122,21 +120,49 @@ impl Pallet { bid.final_ct_amount = buyable_amount; highest_accepted_price = highest_accepted_price.max(bid.original_ct_usd_price); } + Bids::::insert((project_id, &bid.bidder, &bid.id), &bid); bid }) .partition(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))); - // Weight calculation variables - let accepted_bids_count = accepted_bids.len() as u32; - let rejected_bids_count = rejected_bids.len() as u32; - // Refund rejected bids. We do it here, so we don't have to calculate all the project // prices and then fail to refund the bids. + let total_rejected_bids = rejected_bids.len() as u32; for bid in rejected_bids.into_iter() { Self::refund_bid(&bid, project_id, &project_account)?; Bids::::remove((project_id, &bid.bidder, &bid.id)); } + ProjectsDetails::::mutate(project_id, |maybe_info| -> DispatchResult { + if let Some(info) = maybe_info { + info.remaining_contribution_tokens.saturating_reduce(bid_token_amount_sum); + info.auction_round_info = AuctionRoundInfo { + is_oversubscribed: if highest_accepted_price == project_metadata.minimum_price { + IsOversubscribed::No + } else { + IsOversubscribed::Yes { total_usd_bid: bid_usd_value_sum } + }, + }; + Ok(()) + } else { + Err(Error::::ProjectDetailsNotFound.into()) + } + })?; + + Ok((accepted_bids.len() as u32, total_rejected_bids)) + } + + /// Calculates the price (in USD) of contribution tokens for the Community and Remainder Rounds + pub fn calculate_weighted_average_price(project_id: ProjectId) -> Result { + let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectMetadataNotFound)?; + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + // Rejected bids were deleted in the previous block. + let accepted_bids = Bids::::iter_prefix_values((project_id,)).collect_vec(); + let project_account = Self::fund_account_id(project_id); + let plmc_price = T::PriceProvider::get_decimals_aware_price(PLMC_FOREIGN_ID, USD_DECIMALS, PLMC_DECIMALS) + .ok_or(Error::::PriceNotFound)?; + let is_oversubscribed = project_details.auction_round_info.is_oversubscribed; + // Calculate the weighted price of the token for the next funding rounds, using winning bids. // for example: if there are 3 winning bids, // A: 10K tokens @ USD15 per token = 150K USD value @@ -155,31 +181,38 @@ impl Pallet { // lastly, sum all the weighted prices to get the final weighted price for the next funding round // 3 + 10.6 + 2.6 = 16.333... - let calc_weighted_price_fn = |bid: &BidInfoOf| -> PriceOf { - let ticket_size = bid.original_ct_usd_price.saturating_mul_int(bid.final_ct_amount); - let bid_weight = ::saturating_from_rational(ticket_size, bid_usd_value_sum); - let weighted_price = bid.original_ct_usd_price.saturating_mul(bid_weight); - weighted_price - }; - let mut weighted_token_price = if highest_accepted_price == project_metadata.minimum_price { - project_metadata.minimum_price - } else { - accepted_bids - .iter() - .map(calc_weighted_price_fn) - .fold(Zero::zero(), |a: T::Price, b: T::Price| a.saturating_add(b)) + + // After reading from storage all accepted bids when calculating the weighted price of each bid, we store them here + let mut weighted_token_price = match is_oversubscribed { + IsOversubscribed::No => project_metadata.minimum_price, + IsOversubscribed::Yes { total_usd_bid } => { + let calc_weighted_price_fn = |bid: &BidInfoOf| -> PriceOf { + let ticket_size = bid.original_ct_usd_price.saturating_mul_int(bid.final_ct_amount); + let bid_weight = + ::saturating_from_rational(ticket_size, total_usd_bid); + let weighted_price = bid.original_ct_usd_price.saturating_mul(bid_weight); + weighted_price + }; + + accepted_bids + .iter() + .map(calc_weighted_price_fn) + .fold(Zero::zero(), |a: PriceOf, b: PriceOf| a.saturating_add(b)) + }, }; - // We are 99% sure that the price cannot be less than minimum if some accepted bids have higher price, but rounding + + // We are 99% sure that the price cannot be less than the minimum if some accepted bids have higher price, but rounding // errors are strange, so we keep this just in case. if weighted_token_price < project_metadata.minimum_price { weighted_token_price = project_metadata.minimum_price; - } + }; let mut final_total_funding_reached_by_bids = BalanceOf::::zero(); - // Update storage - // Update the bid in the storage - for mut bid in accepted_bids.into_iter() { + let mut total_accepted_bids = 0u32; + for mut bid in accepted_bids { + total_accepted_bids += 1; + if bid.final_ct_usd_price > weighted_token_price || matches!(bid.status, BidStatus::PartiallyAccepted(..)) { if bid.final_ct_usd_price > weighted_token_price { bid.final_ct_usd_price = weighted_token_price; @@ -247,7 +280,6 @@ impl Pallet { ProjectsDetails::::mutate(project_id, |maybe_info| -> DispatchResult { if let Some(info) = maybe_info { info.weighted_average_price = Some(weighted_token_price); - info.remaining_contribution_tokens.saturating_reduce(bid_token_amount_sum); info.funding_amount_reached_usd.saturating_accrue(final_total_funding_reached_by_bids); Ok(()) } else { @@ -255,7 +287,7 @@ impl Pallet { } })?; - Ok((accepted_bids_count, rejected_bids_count)) + Ok(total_accepted_bids) } /// Refund a bid because of `reason`. diff --git a/pallets/funding/src/instantiator/async_features.rs b/pallets/funding/src/instantiator/async_features.rs index d15235e1a..96e64f069 100644 --- a/pallets/funding/src/instantiator/async_features.rs +++ b/pallets/funding/src/instantiator/async_features.rs @@ -171,7 +171,6 @@ pub async fn async_create_evaluating_project< let mut inst = instantiator.lock().await; inst.start_evaluation(project_id, issuer).unwrap(); - let now = inst.current_block(); project_id } @@ -204,7 +203,7 @@ pub async fn async_start_auction< assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionInitializePeriod); - inst.execute(|| crate::Pallet::::do_auction_opening(caller.clone(), project_id).unwrap()); + inst.execute(|| crate::Pallet::::do_start_auction_opening(caller.clone(), project_id).unwrap()); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionOpening); @@ -281,35 +280,32 @@ pub async fn async_start_community_funding< project_id: ProjectId, ) -> Result<(), DispatchError> { let mut inst = instantiator.lock().await; + if let Some(update_block) = inst.get_update_block(project_id, &UpdateType::AuctionClosingStart) { + let notify = Arc::new(Notify::new()); + block_orchestrator.add_awaiting_project(update_block, notify.clone()).await; + drop(inst); + notify.notified().await; + } + let mut inst = instantiator.lock().await; + if let Some(update_block) = inst.get_update_block(project_id, &UpdateType::AuctionClosingEnd) { + let notify = Arc::new(Notify::new()); + block_orchestrator.add_awaiting_project(update_block, notify.clone()).await; + drop(inst); + notify.notified().await; + } + let mut inst = instantiator.lock().await; + if let Some(update_block) = inst.get_update_block(project_id, &UpdateType::CommunityFundingStart) { + let notify = Arc::new(Notify::new()); + block_orchestrator.add_awaiting_project(update_block, notify.clone()).await; + drop(inst); + notify.notified().await; + } + let mut inst = instantiator.lock().await; - let update_block = inst.get_update_block(project_id, &UpdateType::AuctionClosingStart).unwrap(); - let closing_start = update_block; - - let notify = Arc::new(Notify::new()); - - block_orchestrator.add_awaiting_project(closing_start, notify.clone()).await; - - // Wait for the notification that our desired block was reached to continue - - drop(inst); - - notify.notified().await; - - inst = instantiator.lock().await; - let update_block = inst.get_update_block(project_id, &UpdateType::CommunityFundingStart).unwrap(); - let community_start = update_block; - - let notify = Arc::new(Notify::new()); - - block_orchestrator.add_awaiting_project(community_start, notify.clone()).await; - - drop(inst); - - notify.notified().await; - - inst = instantiator.lock().await; - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound); + ensure!( + inst.get_project_details(project_id).status == ProjectStatus::CommunityRound, + DispatchError::from("Auction failed") + ); Ok(()) } @@ -766,10 +762,11 @@ pub async fn async_create_project_at< ) -> ProjectId { let time_to_new_project: BlockNumberFor = Zero::zero(); let time_to_evaluation: BlockNumberFor = time_to_new_project + Zero::zero(); - // we immediately start the auction, so we dont wait for T::AuctionInitializePeriodDuration. + // we immediately start the auction, so we don't wait for T::AuctionInitializePeriodDuration. let time_to_auction: BlockNumberFor = time_to_evaluation + ::EvaluationDuration::get(); + let wap_calculation_duration: BlockNumberFor = One::one(); let time_to_community: BlockNumberFor = - time_to_auction + ::AuctionOpeningDuration::get() + ::AuctionClosingDuration::get(); + time_to_auction + ::AuctionOpeningDuration::get() + ::AuctionClosingDuration::get() + wap_calculation_duration; let time_to_remainder: BlockNumberFor = time_to_community + ::CommunityFundingDuration::get(); let time_to_finish: BlockNumberFor = time_to_remainder + ::RemainderFundingDuration::get() + @@ -791,13 +788,15 @@ pub async fn async_create_project_at< block_orchestrator.add_awaiting_project(now + time_to_finish - time_to_evaluation, notify.clone()).await; // Wait for the notification that our desired block was reached to continue notify.notified().await; - let now = mutex_inst.lock().await.current_block(); - async_create_evaluating_project( + let project_id = async_create_evaluating_project( mutex_inst.clone(), test_project_params.metadata, test_project_params.issuer, ) - .await + .await; + let project_details = inst.get_project_details(project_id); + let x = 10; + project_details }, ProjectStatus::AuctionOpening | ProjectStatus::AuctionClosing => { let notify = Arc::new(Notify::new()); diff --git a/pallets/funding/src/instantiator/chain_interactions.rs b/pallets/funding/src/instantiator/chain_interactions.rs index d0a70f8ec..bc0fbd707 100644 --- a/pallets/funding/src/instantiator/chain_interactions.rs +++ b/pallets/funding/src/instantiator/chain_interactions.rs @@ -292,6 +292,7 @@ impl< total_bonded_plmc: Zero::zero(), evaluators_outcome: EvaluatorsOutcome::Unchanged, }, + auction_round_info: AuctionRoundInfoOf:: { is_oversubscribed: IsOversubscribed::No }, funding_end_block: None, parachain_id: None, migration_readiness_check: None, @@ -451,7 +452,7 @@ impl< assert_eq!(self.get_project_details(project_id).status, ProjectStatus::AuctionInitializePeriod); - self.execute(|| crate::Pallet::::do_auction_opening(caller, project_id).unwrap()); + self.execute(|| crate::Pallet::::do_start_auction_opening(caller, project_id).unwrap()); assert_eq!(self.get_project_details(project_id).status, ProjectStatus::AuctionOpening); @@ -515,14 +516,14 @@ impl< pub fn start_community_funding(&mut self, project_id: ProjectId) -> Result<(), DispatchError> { if let Some(update_block) = self.get_update_block(project_id, &UpdateType::AuctionClosingStart) { - self.execute(|| frame_system::Pallet::::set_block_number(update_block - One::one())); - self.advance_time(1u32.into()).unwrap(); + self.jump_to_block(update_block); + } + if let Some(update_block) = self.get_update_block(project_id, &UpdateType::AuctionClosingEnd) { + self.jump_to_block(update_block); + } + if let Some(update_block) = self.get_update_block(project_id, &UpdateType::CommunityFundingStart) { + self.jump_to_block(update_block); } - let Some(update_block) = self.get_update_block(project_id, &UpdateType::CommunityFundingStart) else { - unreachable!() - }; - self.execute(|| frame_system::Pallet::::set_block_number(update_block - One::one())); - self.advance_time(1u32.into()).unwrap(); ensure!( self.get_project_details(project_id).status == ProjectStatus::CommunityRound, diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index eb437ef25..a5d544353 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -139,7 +139,6 @@ use sp_std::{marker::PhantomData, prelude::*}; pub use types::*; use xcm::v3::{opaque::Instruction, prelude::*, SendXcm}; - #[cfg(test)] pub mod mock; pub mod storage_migrations; @@ -151,10 +150,10 @@ pub mod tests; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod functions; #[cfg(any(feature = "runtime-benchmarks", feature = "std"))] pub mod instantiator; pub mod traits; -mod functions; pub type AccountIdOf = ::AccountId; pub type ProjectId = u32; @@ -172,9 +171,17 @@ pub type EvaluatorsOutcomeOf = EvaluatorsOutcome>; pub type TicketSizeOf = TicketSize>; pub type ProjectMetadataOf = ProjectMetadata>, BalanceOf, PriceOf, AccountIdOf, Cid>; -pub type ProjectDetailsOf = - ProjectDetails, Did, BlockNumberFor, PriceOf, BalanceOf, EvaluationRoundInfoOf>; +pub type ProjectDetailsOf = ProjectDetails< + AccountIdOf, + Did, + BlockNumberFor, + PriceOf, + BalanceOf, + EvaluationRoundInfoOf, + AuctionRoundInfoOf, +>; pub type EvaluationRoundInfoOf = EvaluationRoundInfo>; +pub type AuctionRoundInfoOf = AuctionRoundInfo>; pub type EvaluationInfoOf = EvaluationInfo, BalanceOf, BlockNumberFor>; pub type BidInfoOf = BidInfo, PriceOf, AccountIdOf, BlockNumberFor, MultiplierOf>; @@ -889,7 +896,7 @@ pub mod pallet { let (account, _did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); - Self::do_auction_opening(account, project_id) + Self::do_start_auction_opening(account, project_id) } /// Bond PLMC for a project in the evaluation stage @@ -1173,16 +1180,19 @@ pub mod pallet { #[pallet::weight(WeightInfoOf::::start_auction_manually(::MaxProjectsToUpdateInsertionAttempts::get() - 1))] pub fn root_do_auction_opening(origin: OriginFor, project_id: ProjectId) -> DispatchResultWithPostInfo { ensure_root(origin)?; - Self::do_auction_opening(T::PalletId::get().into_account_truncating(), project_id) + Self::do_start_auction_opening(T::PalletId::get().into_account_truncating(), project_id) } #[pallet::call_index(29)] #[pallet::weight(WeightInfoOf::::start_auction_closing_phase( ::MaxProjectsToUpdateInsertionAttempts::get() - 1, ))] - pub fn root_do_auction_closing(origin: OriginFor, project_id: ProjectId) -> DispatchResultWithPostInfo { + pub fn root_do_start_auction_closing( + origin: OriginFor, + project_id: ProjectId, + ) -> DispatchResultWithPostInfo { ensure_root(origin)?; - Self::do_auction_closing(project_id) + Self::do_start_auction_closing(project_id) } #[pallet::call_index(30)] @@ -1290,7 +1300,8 @@ pub mod pallet { // AuctionInitializePeriod -> AuctionOpening // Only if it wasn't first handled by user extrinsic UpdateType::AuctionOpeningStart => { - let call = Self::do_auction_opening(T::PalletId::get().into_account_truncating(), project_id); + let call = + Self::do_start_auction_opening(T::PalletId::get().into_account_truncating(), project_id); let fallback_weight = Call::::root_do_auction_opening { project_id }.get_dispatch_info().weight; update_weight(&mut used_weight, call, fallback_weight); @@ -1298,9 +1309,16 @@ pub mod pallet { // AuctionOpening -> AuctionClosing UpdateType::AuctionClosingStart => { - let call = Self::do_auction_closing(project_id); + let call = Self::do_start_auction_closing(project_id); + let fallback_weight = + Call::::root_do_start_auction_closing { project_id }.get_dispatch_info().weight; + update_weight(&mut used_weight, call, fallback_weight); + }, + + UpdateType::AuctionClosingEnd => { + let call = Self::do_end_auction_closing(project_id); let fallback_weight = - Call::::root_do_auction_closing { project_id }.get_dispatch_info().weight; + Call::::root_do_start_auction_closing { project_id }.get_dispatch_info().weight; update_weight(&mut used_weight, call, fallback_weight); }, diff --git a/pallets/funding/src/tests/2_evaluation.rs b/pallets/funding/src/tests/2_evaluation.rs index 725e3fd79..ef6d861d6 100644 --- a/pallets/funding/src/tests/2_evaluation.rs +++ b/pallets/funding/src/tests/2_evaluation.rs @@ -325,6 +325,7 @@ mod start_evaluation_extrinsic { total_bonded_plmc: 0u128, evaluators_outcome: EvaluatorsOutcome::Unchanged, }, + auction_round_info: AuctionRoundInfoOf:: { is_oversubscribed: IsOversubscribed::No }, funding_end_block: None, parachain_id: None, migration_readiness_check: None, diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index 6319bcf32..629fa0463 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -745,7 +745,7 @@ mod start_auction_extrinsic { inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionInitializePeriod); inst.advance_time(1).unwrap(); - inst.execute(|| Pallet::::do_auction_opening(ISSUER_1, project_id)).unwrap(); + inst.execute(|| Pallet::::do_start_auction_opening(ISSUER_1, project_id)).unwrap(); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionOpening); } @@ -765,7 +765,7 @@ mod start_auction_extrinsic { for account in 6000..6010 { inst.execute(|| { - let response = Pallet::::do_auction_opening(account, project_id); + let response = Pallet::::do_start_auction_opening(account, project_id); assert_noop!(response, Error::::NotIssuer); }); } @@ -782,7 +782,7 @@ mod start_auction_extrinsic { let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1); inst.execute(|| { assert_noop!( - PolimecFunding::do_auction_opening(ISSUER_1, project_id), + PolimecFunding::do_start_auction_opening(ISSUER_1, project_id), Error::::TransitionPointNotSet ); }); @@ -795,7 +795,7 @@ mod start_auction_extrinsic { inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); inst.execute(|| { assert_noop!( - PolimecFunding::do_auction_opening(ISSUER_1, project_id), + PolimecFunding::do_start_auction_opening(ISSUER_1, project_id), Error::::TransitionPointNotSet ); }); diff --git a/pallets/funding/src/tests/4_community.rs b/pallets/funding/src/tests/4_community.rs index 5111d6f5b..e041482b7 100644 --- a/pallets/funding/src/tests/4_community.rs +++ b/pallets/funding/src/tests/4_community.rs @@ -765,8 +765,7 @@ mod community_contribute_extrinsic { ) .unwrap(); inst.bid_for_users(project_id, failing_bids_after_random_end).unwrap(); - inst.advance_time(2).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound); + inst.start_community_funding(project_id).unwrap(); // Some low amount of plmc and usdt to cover a purchase of 10CTs. let plmc_mints = vec![ diff --git a/pallets/funding/src/tests/misc.rs b/pallets/funding/src/tests/misc.rs index f8263b7cf..383016854 100644 --- a/pallets/funding/src/tests/misc.rs +++ b/pallets/funding/src/tests/misc.rs @@ -412,7 +412,6 @@ mod async_tests { ]; let (project_ids, mut inst) = create_multiple_projects_at(inst, project_params); - let now = inst.current_block(); dbg!(inst.get_project_details(project_ids[0]).status); dbg!(inst.get_project_details(project_ids[1]).status); diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index bc95e8e33..769968855 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -303,6 +303,7 @@ pub mod storage_types { Price: FixedPointNumber, Balance: BalanceT, EvaluationRoundInfo, + AuctionRoundInfo, > { pub issuer_account: AccountId, pub issuer_did: Did, @@ -322,6 +323,7 @@ pub mod storage_types { pub funding_amount_reached_usd: Balance, /// Information about the total amount bonded, and the outcome in regards to reward/slash/nothing pub evaluation_round_info: EvaluationRoundInfo, + pub auction_round_info: AuctionRoundInfo, /// When the Funding Round ends pub funding_end_block: Option, /// ParaId of project @@ -337,6 +339,7 @@ pub mod storage_types { EvaluationEnd, AuctionOpeningStart, AuctionClosingStart, + AuctionClosingEnd, CommunityFundingStart, RemainderFundingStart, FundingEnd, @@ -639,6 +642,7 @@ pub mod inner_types { AuctionInitializePeriod, AuctionOpening, AuctionClosing, + CalculatingWAP, CommunityRound, RemainderRound, FundingFailed, @@ -745,6 +749,7 @@ pub mod inner_types { AuctionInitializePeriod, AuctionOpening, AuctionClosing, + CalculatingWAP, CommunityFunding, RemainderFunding, DecisionPeriod, @@ -775,6 +780,18 @@ pub mod inner_types { pub evaluators_outcome: EvaluatorsOutcome, } + // putting the enum inside a struct in case we need to extend it with other fields later on + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] + pub struct AuctionRoundInfo { + // Will be `Some` after the auction closing phase ends and winning bids are calculated + pub is_oversubscribed: IsOversubscribed, + } + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] + pub enum IsOversubscribed { + Yes { total_usd_bid: Balance }, + No, + } + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum EvaluatorsOutcome { Unchanged,