From c022c0528397d3e59b31d379b99a4b672ee46107 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Tue, 19 Mar 2024 17:24:42 +0100 Subject: [PATCH 01/21] settlement first setup --- pallets/funding/src/lib.rs | 112 +++++++---- pallets/funding/src/settlement.rs | 306 ++++++++++++++++++++++++++++++ 2 files changed, 377 insertions(+), 41 deletions(-) create mode 100644 pallets/funding/src/settlement.rs diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 9ee5a6ee9..676c4080a 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -137,8 +137,10 @@ use sp_std::{marker::PhantomData, prelude::*}; use traits::DoRemainingOperation; pub use types::*; use xcm::v3::{opaque::Instruction, prelude::*, SendXcm}; +use parachains_common::MAXIMUM_BLOCK_WEIGHT; pub mod functions; +pub mod settlement; #[cfg(test)] pub mod mock; @@ -203,8 +205,8 @@ pub mod pallet { use local_macros::*; use sp_arithmetic::Percent; use sp_runtime::{ - traits::{Convert, ConvertBack, Get}, - DispatchErrorWithPostInfo, + traits::{Convert, ConvertBack, Get}, + DispatchErrorWithPostInfo }; #[cfg(any(feature = "runtime-benchmarks", feature = "std"))] @@ -548,6 +550,9 @@ pub mod pallet { ValueQuery, >; + #[pallet::storage] + pub type ProjectSettlementQueue = StorageValue<_, ProjectId>; + #[pallet::storage] /// Migrations sent and awaiting for confirmation pub type UnconfirmedMigrations = StorageMap<_, Blake2_128Concat, QueryId, ProjectMigrationOriginsOf>; @@ -1608,48 +1613,73 @@ pub mod pallet { fn on_idle(_now: BlockNumberFor, max_weight: Weight) -> Weight { let mut remaining_weight = max_weight; - let projects_needing_cleanup = ProjectsDetails::::iter() - .filter_map(|(project_id, info)| match info.cleanup { - cleaner if >::has_remaining_operations(&cleaner) => - Some((project_id, cleaner)), - _ => None, - }) - .collect::>(); - - let projects_amount = projects_needing_cleanup.len() as u64; - if projects_amount == 0 { - return max_weight; - } - - let mut max_weight_per_project = remaining_weight.saturating_div(projects_amount); - - for (remaining_projects, (project_id, mut cleaner)) in - projects_needing_cleanup.into_iter().enumerate().rev() - { - // TODO: Create this benchmark - // let mut consumed_weight = WeightInfoOf::::insert_cleaned_project(); - let mut consumed_weight = Weight::from_parts(6_034_000, 0); - while !consumed_weight.any_gt(max_weight_per_project) { - if let Ok(weight) = >::do_one_operation(&mut cleaner, project_id) - { - consumed_weight.saturating_accrue(weight); - } else { - break; + // We want at least 5% of max block weight + let at_least = MAXIMUM_BLOCK_WEIGHT.saturating_div(20); + if remaining_weight.all_gt(at_least) { + return Weight::zero(); + }; + + if let Some(project_id) = ProjectSettlementQueue::::get() { + if let Some(project_details) = ProjectsDetails::::get(project_id) { + let eval_iter = Evaluations::::iter_key_prefix((project_id,)); + while remaining_weight.any_gt(at_least) { + + match project_details.status { + ProjectStatus::FundingSuccessful => + } } - } - - let mut details = - if let Some(details) = ProjectsDetails::::get(project_id) { details } else { continue }; - details.cleanup = cleaner; - ProjectsDetails::::insert(project_id, details); + - remaining_weight = remaining_weight.saturating_sub(consumed_weight); - if remaining_projects > 0 { - max_weight_per_project = remaining_weight.saturating_div(remaining_projects as u64); + } else { + log::error!("Project details not found for project_id: {:?} in ProjectSettlementQueue", project_id); } - } - - max_weight.saturating_sub(remaining_weight) + }; + + + remaining_weight + + // let projects_needing_cleanup = ProjectsDetails::::iter() + // .filter_map(|(project_id, info)| match info.cleanup { + // cleaner if >::has_remaining_operations(&cleaner) => + // Some((project_id, cleaner)), + // _ => None, + // }) + // .collect::>(); + + // let projects_amount = projects_needing_cleanup.len() as u64; + // if projects_amount == 0 { + // return max_weight; + // } + + // let mut max_weight_per_project = remaining_weight.saturating_div(projects_amount); + + // for (remaining_projects, (project_id, mut cleaner)) in + // projects_needing_cleanup.into_iter().enumerate().rev() + // { + // // TODO: Create this benchmark + // // let mut consumed_weight = WeightInfoOf::::insert_cleaned_project(); + // let mut consumed_weight = Weight::from_parts(6_034_000, 0); + // while !consumed_weight.any_gt(max_weight_per_project) { + // if let Ok(weight) = >::do_one_operation(&mut cleaner, project_id) + // { + // consumed_weight.saturating_accrue(weight); + // } else { + // break; + // } + // } + + // let mut details = + // if let Some(details) = ProjectsDetails::::get(project_id) { details } else { continue }; + // details.cleanup = cleaner; + // ProjectsDetails::::insert(project_id, details); + + // remaining_weight = remaining_weight.saturating_sub(consumed_weight); + // if remaining_projects > 0 { + // max_weight_per_project = remaining_weight.saturating_div(remaining_projects as u64); + // } + // } + + // max_weight.saturating_sub(remaining_weight) } } diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs new file mode 100644 index 000000000..a249d1829 --- /dev/null +++ b/pallets/funding/src/settlement.rs @@ -0,0 +1,306 @@ +use super::*; +use crate::{ + traits::{BondingRequirementCalculation, ProvideAssetPrice, VestingDurationCalculation}, + ProjectStatus::FundingSuccessful, +}; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, PostDispatchInfo}, + ensure, + pallet_prelude::*, + traits::{ + fungible::{InspectHold, Mutate, MutateHold as FungibleMutateHold}, + fungibles::{ + metadata::{MetadataDeposit, Mutate as MetadataMutate}, + Create, Inspect, Mutate as FungiblesMutate, + }, + tokens::{Fortitude, Precision, Preservation, Restriction}, + Get, + }, +}; +use sp_runtime::{traits::Zero, Perquintill}; +use polimec_common::{ + migration_types::{MigrationInfo, MigrationOrigin, Migrations, ParticipationType}, + ReleaseSchedule, +}; + +impl Pallet { + pub fn do_settlement_success_bidder(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { + + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + // Ensure that: + // 1. The project is in the FundingSuccessful state + // 2. The bid is in the Accepted or PartiallyAccepted state + // 3. The contribution token exists + ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); + ensure!(matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), Error::::NotAllowed); + ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); + + let bidder = bid.bidder; + + // Calculate the vesting info and add the release schedule + let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; + let vest_info = + Self::calculate_vesting_info(&bidder, bid.multiplier, bid.plmc_bond).map_err(|_| Error::::BadMath)?; + + T::Vesting::add_release_schedule( + &bidder, + vest_info.total_amount, + vest_info.amount_per_block, + funding_end_block, + HoldReason::Participation(project_id).into(), + )?; + + // Mint the contribution tokens + Self::mint_ct_tokens(project_id, &bidder, bid.final_ct_amount)?; + + // Payout the bid funding asset amount to the project account + Self::release_funding_asset(project_id, &project_details.issuer_account, bid.funding_asset_amount_locked, bid.funding_asset)?; + + // TODO: Create MigrationInfo + + Bids::::remove((project_id, bidder, bid.id)); + + // TODO: Emit an event + + Ok(()) + } + + pub fn do_settlement_failure_bidder(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!( + matches!(project_details.status, ProjectStatus::FundingFailed), + Error::::NotAllowed + ); + + let bidder = bid.bidder; + + // Release the held future ct deposit + Self::release_future_ct_deposit(project_id, &bidder)?; + + if matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) { + // Return the funding assets to the bidder + Self::release_funding_asset(project_id, &bidder, bid.funding_asset_amount_locked, bid.funding_asset)?; + + // Release the held PLMC bond + Self::release_bond(project_id, &bidder, bid.plmc_bond)?; + } + + // Remove the bid from the storage + Bids::::remove((project_id, bidder, bid.id)); + + // TODO: Emit an event + + Ok(()) + } + + pub fn do_settlement_success_contributor(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + // Ensure that: + // 1. The project is in the FundingSuccessful state + // 2. The contribution token exists + ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); + ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); + + let contributor = contribution.contributor; + + // Calculate the vesting info and add the release schedule + let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; + let vest_info = + Self::calculate_vesting_info(&contributor, contribution.multiplier, contribution.plmc_bond).map_err(|_| Error::::BadMath)?; + + T::Vesting::add_release_schedule( + &contributor, + vest_info.total_amount, + vest_info.amount_per_block, + funding_end_block, + HoldReason::Participation(project_id).into(), + )?; + + // Mint the contribution tokens + Self::mint_ct_tokens(project_id, &contributor, contribution.ct_amount)?; + + // Payout the bid funding asset amount to the project account + Self::release_funding_asset(project_id, &project_details.issuer_account, contribution.funding_asset_amount, contribution.funding_asset)?; + + Contributions::::remove((project_id, contributor, contribution.id)); + + Ok(()) + } + + pub fn do_setllement_failure_contributor(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!( + matches!(project_details.status, ProjectStatus::FundingFailed), + Error::::NotAllowed + ); + + // Check if the bidder has a future deposit held + let contributor = contribution.contributor; + + // Release the held future ct deposit + Self::release_future_ct_deposit(project_id, &contributor)?; + + // Return the funding assets to the contributor + Self::release_funding_asset(project_id, &contributor, contribution.funding_asset_amount, contribution.funding_asset)?; + + // Release the held PLMC bond + Self::release_bond(project_id, &contributor, contribution.plmc_bond)?; + + + // Remove the bid from the storage + Contributions::::remove((project_id, contributor, contribution.id)); + + // TODO: Emit an event + + Ok(()) + } + + pub fn do_settlement_success_evaluator(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::::NotAllowed); + + // Based on the results of the funding round, the evaluator is either: + // 1. Slashed + // 2. Rewarded with CT tokens + // 3. Not slashed or Rewarded. + let bond = match project_details.evaluation_round_info.evaluators_outcome { + EvaluatorsOutcome::Slashed => Self::slash_evaluator(project_id, &evaluation)?, + EvaluatorsOutcome::Rewarded(info) => Self::reward_evaluator(project_id, &evaluation, info)?, + EvaluatorsOutcome::Unchanged => evaluation.current_plmc_bond, + }; + + // Release the held PLMC bond + T::NativeCurrency::release( + &HoldReason::Evaluation(project_id).into(), + &evaluation.evaluator, + bond, + Precision::Exact, + )?; + + Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); + + // TODO: Emit an event + + Ok(()) + } + + pub fn do_settlement_failure_evaluator(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!( + matches!(project_details.status, ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed), + Error::::NotAllowed + ); + + let bond; + if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { + bond = Self::slash_evaluator(project_id, &evaluation)?; + } else { + bond = evaluation.current_plmc_bond; + } + + // Release the held future ct deposit + Self::release_future_ct_deposit(project_id, &evaluation.evaluator)?; + + // Release the held PLMC bond + T::NativeCurrency::release( + &HoldReason::Evaluation(project_id).into(), + &evaluation.evaluator, + bond, + Precision::Exact, + )?; + + Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); + + // TODO: Emit an event + + Ok(()) + } + + fn mint_ct_tokens(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { + if !T::ContributionTokenCurrency::contains(&project_id, participant) { + Self::release_future_ct_deposit(project_id, participant)?; + T::ContributionTokenCurrency::touch(project_id, participant.clone(), participant.clone())?; + } + T::ContributionTokenCurrency::mint_into(project_id, participant, amount)?; + Ok(()) + } + + fn release_future_ct_deposit(project_id: ProjectId, participant: &AccountIdOf) -> DispatchResult { + let held_plmc = T::NativeCurrency::balance_on_hold(&HoldReason::FutureDeposit(project_id).into(), participant); + ensure!(held_plmc > Zero::zero(), Error::::NoFutureDepositHeld); + + // Return the held deposit to the bidder + T::NativeCurrency::release( + &HoldReason::FutureDeposit(project_id).into(), + participant, + T::ContributionTokenCurrency::deposit_required(project_id), + Precision::Exact, + )?; + Ok(()) + } + + fn release_funding_asset(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf, asset: AcceptedFundingAsset) -> DispatchResult { + let project_pot = Self::fund_account_id(project_id); + T::FundingCurrency::transfer( + asset.to_assethub_id(), + &project_pot, + &participant, + amount, + Preservation::Expendable, + )?; + Ok(()) + } + + fn release_bond(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { + // Release the held PLMC bond + T::NativeCurrency::release( + &HoldReason::Participation(project_id).into(), + &participant, + amount, + Precision::Exact, + )?; + Ok(()) + } + + fn slash_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf) -> Result, DispatchError> { + + let slash_percentage = T::EvaluatorSlash::get(); + let treasury_account = T::ProtocolGrowthTreasury::get(); + + // * Calculate variables * + // We need to make sure that the current PLMC bond is always >= than the slash amount. + let slashed_amount = slash_percentage * evaluation.original_plmc_bond; + + T::NativeCurrency::transfer_on_hold( + &HoldReason::Evaluation(project_id).into(), + &evaluation.evaluator, + &treasury_account, + slashed_amount, + Precision::Exact, + Restriction::Free, + Fortitude::Force, + )?; + + Ok(evaluation.current_plmc_bond.saturating_sub(slashed_amount)) + } + + fn reward_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf, info: RewardInfoOf) -> Result, DispatchError> { + + // * Calculate variables * + let early_reward_weight = Perquintill::from_rational( + evaluation.early_usd_amount, + info.early_evaluator_total_bonded_usd + ); + let normal_reward_weight = Perquintill::from_rational( + evaluation.late_usd_amount.saturating_add(evaluation.early_usd_amount), + info.normal_evaluator_total_bonded_usd, + ); + let early_evaluators_rewards = early_reward_weight * info.early_evaluator_reward_pot; + let normal_evaluators_rewards = normal_reward_weight * info.normal_evaluator_reward_pot; + let total_reward_amount = early_evaluators_rewards.saturating_add(normal_evaluators_rewards); + + Self::mint_ct_tokens(project_id, &evaluation.evaluator, total_reward_amount)?; + + Ok(evaluation.current_plmc_bond) + } +} \ No newline at end of file From 1a5837ea1a3f2d678657bb13c799f5b77bb34a92 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Wed, 20 Mar 2024 09:06:39 +0100 Subject: [PATCH 02/21] set on_idle --- pallets/funding/src/lib.rs | 64 ++++++++++++++++++++++++++++--- pallets/funding/src/settlement.rs | 2 +- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 676c4080a..a18cf6277 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -1615,19 +1615,71 @@ pub mod pallet { // We want at least 5% of max block weight let at_least = MAXIMUM_BLOCK_WEIGHT.saturating_div(20); - if remaining_weight.all_gt(at_least) { + if !remaining_weight.any_lt(at_least) { return Weight::zero(); }; if let Some(project_id) = ProjectSettlementQueue::::get() { if let Some(project_details) = ProjectsDetails::::get(project_id) { - let eval_iter = Evaluations::::iter_key_prefix((project_id,)); - while remaining_weight.any_gt(at_least) { - - match project_details.status { - ProjectStatus::FundingSuccessful => + let mut eval_iter = Evaluations::::iter_prefix((project_id,)); + + while let Some((_, evaluation)) = eval_iter.next() { + if remaining_weight.any_lt(at_least) { + return Weight::zero(); + } + let _ = match project_details.status { + ProjectStatus::FundingSuccessful => { + Self::do_settlement_success_evaluator(evaluation, project_id) + }, + ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => { + Self::do_settlement_failure_evaluator(evaluation, project_id) + }, + _ => { + log::error!("Project status not allowed for settlement: {:?}", project_details.status); + Ok(()) + } + }; + } + + let mut bid_iter = Bids::::iter_prefix((project_id,)); + + while let Some((_, bid)) = bid_iter.next() { + if remaining_weight.any_lt(at_least) { + return Weight::zero(); } + let _ = match project_details.status { + ProjectStatus::FundingSuccessful => { + Self::do_settlement_success_bidder(bid, project_id) + }, + ProjectStatus::FundingFailed => { + Self::do_settlement_failure_bidder(bid, project_id) + }, + _ => { + log::error!("Project status not allowed for settlement: {:?}", project_details.status); + Ok(()) + } + }; } + + let mut contribution_iter = Contributions::::iter_prefix((project_id,)); + + while let Some((_, contribution)) = contribution_iter.next() { + if remaining_weight.any_lt(at_least) { + return Weight::zero(); + } + let _ = match project_details.status { + ProjectStatus::FundingSuccessful => { + Self::do_settlement_success_contributor(contribution, project_id) + }, + ProjectStatus::FundingFailed => { + Self::do_settlement_failure_contributor(contribution, project_id) + }, + _ => { + log::error!("Project status not allowed for settlement: {:?}", project_details.status); + Ok(()) + } + }; + }; } else { diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index a249d1829..8ebe7d45d 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -127,7 +127,7 @@ impl Pallet { Ok(()) } - pub fn do_setllement_failure_contributor(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { + pub fn do_settlement_failure_contributor(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; ensure!( matches!(project_details.status, ProjectStatus::FundingFailed), From 7af71a2acb532127d43db178053b9e4b8c26594c Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Wed, 20 Mar 2024 10:45:41 +0100 Subject: [PATCH 03/21] fix single test and implement settlement queue --- pallets/funding/src/functions.rs | 4 ++++ pallets/funding/src/lib.rs | 17 +++++++++++---- pallets/funding/src/mock.rs | 1 + pallets/funding/src/settlement.rs | 32 ++++++++++++++-------------- pallets/funding/src/tests.rs | 35 +++++++++++++++++-------------- 5 files changed, 53 insertions(+), 36 deletions(-) diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index 5330bd4ac..b02df46c3 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -306,6 +306,7 @@ impl Pallet { project_details.status = ProjectStatus::EvaluationFailed; project_details.cleanup = Cleaner::Failure(CleanerState::Initialized(PhantomData::)); ProjectsDetails::::insert(project_id, project_details); + ProjectSettlementQueue::::try_append(project_id).map_err(|_| Error::::TooManyProjectsInSettlementQueue)?; // * Emit events * Self::deposit_event(Event::EvaluationFailed { project_id }); @@ -859,11 +860,14 @@ impl Pallet { liquidity_pools_ct_amount, )?; + ProjectSettlementQueue::::try_append(project_id).map_err(|_| Error::::TooManyProjectsInSettlementQueue)?; + Ok(PostDispatchInfo { actual_weight: Some(WeightInfoOf::::start_settlement_funding_success()), pays_fee: Pays::Yes, }) } else { + ProjectSettlementQueue::::try_append(project_id).map_err(|_| Error::::TooManyProjectsInSettlementQueue)?; Ok(PostDispatchInfo { actual_weight: Some(WeightInfoOf::::start_settlement_funding_failure()), pays_fee: Pays::Yes, diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index a18cf6277..993fba1f3 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -334,6 +334,10 @@ pub mod pallet { #[pallet::constant] type MaxMessageSizeThresholds: Get<(u32, u32)>; + /// Max Projects to add to settlement queue. + #[pallet::constant] + type MaxProjectsToSettle: Get; + /// max iterations for trying to insert a project on the projects_to_update storage #[pallet::constant] type MaxProjectsToUpdateInsertionAttempts: Get; @@ -551,7 +555,7 @@ pub mod pallet { >; #[pallet::storage] - pub type ProjectSettlementQueue = StorageValue<_, ProjectId>; + pub type ProjectSettlementQueue = StorageValue<_, BoundedVec, ValueQuery>; #[pallet::storage] /// Migrations sent and awaiting for confirmation @@ -963,6 +967,8 @@ pub mod pallet { TooManyEvaluationsForProject, /// Reached contribution limit for this user on this project TooManyContributionsForUser, + /// Reached the settlement queue limit. + TooManyProjectsInSettlementQueue, // Participant tried to do a community contribution but it already had a winning bid on the auction round. UserHasWinningBids, // Round transition already happened. @@ -1615,11 +1621,11 @@ pub mod pallet { // We want at least 5% of max block weight let at_least = MAXIMUM_BLOCK_WEIGHT.saturating_div(20); - if !remaining_weight.any_lt(at_least) { + if remaining_weight.any_lt(at_least) { return Weight::zero(); }; - - if let Some(project_id) = ProjectSettlementQueue::::get() { + let project_ids: BoundedVec = ProjectSettlementQueue::::get(); + if let Some(&project_id) = project_ids.first() { if let Some(project_details) = ProjectsDetails::::get(project_id) { let mut eval_iter = Evaluations::::iter_prefix((project_id,)); @@ -1681,6 +1687,9 @@ pub mod pallet { }; }; + ProjectSettlementQueue::::mutate(|queue| { + queue.remove(0); + }); } else { log::error!("Project details not found for project_id: {:?} in ProjectSettlementQueue", project_id); diff --git a/pallets/funding/src/mock.rs b/pallets/funding/src/mock.rs index efc5f8295..4f3b1c669 100644 --- a/pallets/funding/src/mock.rs +++ b/pallets/funding/src/mock.rs @@ -370,6 +370,7 @@ impl Config for TestRuntime { type MaxEvaluationsPerProject = ConstU32<1024>; type MaxEvaluationsPerUser = ConstU32<4>; type MaxMessageSizeThresholds = MaxMessageSizeThresholds; + type MaxProjectsToSettle = ConstU32<100>; type MaxProjectsToUpdateInsertionAttempts = ConstU32<100>; type MaxProjectsToUpdatePerBlock = ConstU32<1>; type Multiplier = Multiplier; diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index 8ebe7d45d..0c0d25d82 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -1,17 +1,13 @@ use super::*; -use crate::{ - traits::{BondingRequirementCalculation, ProvideAssetPrice, VestingDurationCalculation}, - ProjectStatus::FundingSuccessful, -}; + use frame_support::{ - dispatch::{DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, PostDispatchInfo}, + dispatch::{ DispatchResult}, ensure, pallet_prelude::*, traits::{ - fungible::{InspectHold, Mutate, MutateHold as FungibleMutateHold}, + fungible::{InspectHold, MutateHold as FungibleMutateHold}, fungibles::{ - metadata::{MetadataDeposit, Mutate as MetadataMutate}, - Create, Inspect, Mutate as FungiblesMutate, + Inspect, Mutate as FungiblesMutate, }, tokens::{Fortitude, Precision, Preservation, Restriction}, Get, @@ -165,7 +161,7 @@ impl Pallet { // 3. Not slashed or Rewarded. let bond = match project_details.evaluation_round_info.evaluators_outcome { EvaluatorsOutcome::Slashed => Self::slash_evaluator(project_id, &evaluation)?, - EvaluatorsOutcome::Rewarded(info) => Self::reward_evaluator(project_id, &evaluation, info)?, + EvaluatorsOutcome::Rewarded(info) => Self::reward_evaluator(project_id, &evaluation, &info)?, EvaluatorsOutcome::Unchanged => evaluation.current_plmc_bond, }; @@ -284,10 +280,17 @@ impl Pallet { Ok(evaluation.current_plmc_bond.saturating_sub(slashed_amount)) } - fn reward_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf, info: RewardInfoOf) -> Result, DispatchError> { + fn reward_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf, info: &RewardInfoOf) -> Result, DispatchError> { - // * Calculate variables * - let early_reward_weight = Perquintill::from_rational( + + let reward = Self::calculate_evaluator_reward(evaluation, &info); + Self::mint_ct_tokens(project_id, &evaluation.evaluator, reward)?; + + Ok(evaluation.current_plmc_bond) + } + + pub fn calculate_evaluator_reward(evaluation: &EvaluationInfoOf, info: &RewardInfoOf) -> BalanceOf { + let early_reward_weight = Perquintill::from_rational( evaluation.early_usd_amount, info.early_evaluator_total_bonded_usd ); @@ -298,9 +301,6 @@ impl Pallet { let early_evaluators_rewards = early_reward_weight * info.early_evaluator_reward_pot; let normal_evaluators_rewards = normal_reward_weight * info.normal_evaluator_reward_pot; let total_reward_amount = early_evaluators_rewards.saturating_add(normal_evaluators_rewards); - - Self::mint_ct_tokens(project_id, &evaluation.evaluator, total_reward_amount)?; - - Ok(evaluation.current_plmc_bond) + total_reward_amount } } \ No newline at end of file diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index e37f40c92..ee4a1b9a6 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -3908,12 +3908,11 @@ mod funding_end { ); let details = inst.get_project_details(project_id); assert_eq!(details.status, ProjectStatus::FundingSuccessful); - assert_eq!(details.cleanup, Cleaner::NotReady); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.advance_time(10u64).unwrap(); - let details = inst.get_project_details(project_id); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); + let reward_info = match details.evaluation_round_info.evaluators_outcome { + EvaluatorsOutcome::Rewarded(reward_info) => Some(reward_info), + _ => None, + }; let evaluators = evaluations.accounts(); let evaluator_ct_amounts = evaluators @@ -3922,17 +3921,17 @@ mod funding_end { let evaluations = inst.execute(|| { Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() }); - let total_evaluator_ct_rewarded = evaluations - .iter() - .map(|evaluation| evaluation.rewarded_or_slashed) - .map(|reward_or_slash| { - if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { - balance - } else { - Zero::zero() - } - }) - .sum::(); + let total_evaluator_ct_rewarded = match reward_info.clone() { + Some(info) => { + evaluations + .iter() + .map(|evaluation| { + Pallet::::calculate_evaluator_reward(evaluation, &info) + }) + .sum::() + }, + None => Zero::zero() + }; (account, total_evaluator_ct_rewarded) }) @@ -3973,6 +3972,10 @@ mod funding_end { |item, accumulator| accumulator + item.1, ); + inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + assert_eq!(inst.execute(|| {ProjectSettlementQueue::::get().len()}), 1); + inst.advance_time(10u64).unwrap(); + for (account, amount) in all_ct_expectations { let minted = inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); From 922c39982fc52f1f15ce3b990e16617c04822f01 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Wed, 20 Mar 2024 13:09:56 +0100 Subject: [PATCH 04/21] fix some tests --- pallets/funding/src/instantiator.rs | 9 +++++++++ pallets/funding/src/tests.rs | 16 ++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index c972fee9b..40c251323 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1010,6 +1010,15 @@ impl< self.execute(|| ProjectsDetails::::get(project_id).expect("Project details exists")) } + pub fn get_first_settlement_queue_item(&mut self) -> Option{ + self.execute(|| { + match ProjectSettlementQueue::::get().first() { + Some(project_id) => Some(*project_id), + None => None, + } + }) + } + pub fn get_update_block(&mut self, project_id: ProjectId, update_type: &UpdateType) -> Option> { self.execute(|| { ProjectsToUpdate::::iter().find_map(|(block, update_vec)| { diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index ee4a1b9a6..f2f004607 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -1237,9 +1237,10 @@ mod auction { inst.start_remainder_or_end_funding(project_id).unwrap(); inst.finish_funding(project_id).unwrap(); - inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); - let details = inst.get_project_details(project_id); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); + inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + assert_eq!(inst.get_first_settlement_queue_item(), Some(project_id)); + inst.advance_time(10u64).unwrap(); + assert_eq!(inst.get_first_settlement_queue_item(), None); let plmc_locked_for_accepted_bid = MockInstantiator::calculate_auction_plmc_charged_with_given_price(&accepted_bid, final_price); @@ -1257,15 +1258,13 @@ mod auction { assert_close_enough!(schedule.unwrap(), accepted_plmc_amount, Perquintill::from_float(0.99)); let UserToPLMCBalance { account: rejected_user, .. } = plmc_locked_for_rejected_bid[0]; - let schedule_exists = inst + assert!(inst .execute(|| { ::Vesting::total_scheduled_amount( &rejected_user, HoldReason::Participation(project_id).into(), ) - }) - .is_some(); - assert!(!schedule_exists); + }).is_none()); } // We use the already tested instantiator functions to calculate the correct post-wap returns @@ -5679,7 +5678,6 @@ mod ct_migration { inst.advance_time(::SuccessToSettlementTime::get() + 20u64).unwrap(); let project_details = inst.get_project_details(project_id); - assert_eq!(project_details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); inst.execute(|| { assert_ok!(crate::Pallet::::do_set_para_id_for_project( @@ -5704,8 +5702,6 @@ mod ct_migration { default_remainder_buys(), ); inst.advance_time(::SuccessToSettlementTime::get() + 20u64).unwrap(); - let project_details = inst.get_project_details(project_id); - assert_eq!(project_details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); inst.execute(|| { assert_err!( From 17ee93f545bd7f753c08db61159e01f5f6818b4f Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Wed, 20 Mar 2024 17:29:48 +0100 Subject: [PATCH 05/21] wip for remove on_idle --- pallets/funding/src/instantiator.rs | 46 ++++++++++++++++++++++ pallets/funding/src/lib.rs | 60 +---------------------------- 2 files changed, 47 insertions(+), 59 deletions(-) diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index 40c251323..bbd10c183 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1344,6 +1344,52 @@ impl< Ok(()) } + pub fn settle_project(&mut self, project_id: ProjectId) -> Result<(), DispatchError> { + let details = self.get_project_details(project_id); + match details.status { + ProjectStatus::FundingSuccessful => self.settle_successfull_project(details), + ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => self.settle_failed_project(details), + _ => panic!("Project should be in FundingSuccessful, FundingFailed or EvaluationFailed status"), + }; + self. + } + + pub fn assert_project_settled(&mut self, project_id: ProjectId) { + assert_eq!(Evaluations::::iter_prefix_keys((project_id,)).count(), 0); + assert_eq!(Bids::::iter_prefix_keys((project_id,)).count(), 0); + assert_eq!(Contributions::::iter_prefix_keys((project_id,)).count(), 0); + } + + fn settle_successfull_project(&mut self, project_id: ProjectId) -> Result<(), DispatchError> { + Evaluations::::iter_prefix((project_id,)).for_each(|(_, evaluation)| { + Pallet::::do_settlement_success_evaluator(evaluation, project_id); + }); + + Bids::::iter_prefix((project_id,)).for_each(|(_, bid)| { + Pallet::::do_settlement_success_bidder(bid, project_id); + }); + + Contributions::::iter_prefix((project_id,)).for_each(|(_, contribution)| { + Pallet::::do_settlement_success_contributor(contribution, project_id); + }); + } + + fn settle_failed_project(&mut self, details: ProjectDetailsOf) -> Result<(), DispatchError> { + Evaluations::::iter_prefix((project_id,)).for_each(|(_, evaluation)| { + Pallet::::do_settlement_failure_evaluator(evaluation, project_id) + }); + + if details.status == ProjectStatus::FundingFailed { + Bids::::iter_prefix((project_id,)).for_each(|(_, bid)| { + Pallet::::do_settlement_failure_bidder(bid, project_id) + }); + + Contributions::::iter_prefix((project_id,)).for_each(|(_, contribution)| { + Pallet::::do_settlement_failure_contributor(contribution, project_id) + }); + } + } + pub fn create_remainder_contributing_project( &mut self, project_metadata: ProjectMetadataOf, diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 993fba1f3..6e4737f32 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -1627,65 +1627,7 @@ pub mod pallet { let project_ids: BoundedVec = ProjectSettlementQueue::::get(); if let Some(&project_id) = project_ids.first() { if let Some(project_details) = ProjectsDetails::::get(project_id) { - let mut eval_iter = Evaluations::::iter_prefix((project_id,)); - - while let Some((_, evaluation)) = eval_iter.next() { - if remaining_weight.any_lt(at_least) { - return Weight::zero(); - } - let _ = match project_details.status { - ProjectStatus::FundingSuccessful => { - Self::do_settlement_success_evaluator(evaluation, project_id) - }, - ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => { - Self::do_settlement_failure_evaluator(evaluation, project_id) - }, - _ => { - log::error!("Project status not allowed for settlement: {:?}", project_details.status); - Ok(()) - } - }; - } - - let mut bid_iter = Bids::::iter_prefix((project_id,)); - - while let Some((_, bid)) = bid_iter.next() { - if remaining_weight.any_lt(at_least) { - return Weight::zero(); - } - let _ = match project_details.status { - ProjectStatus::FundingSuccessful => { - Self::do_settlement_success_bidder(bid, project_id) - }, - ProjectStatus::FundingFailed => { - Self::do_settlement_failure_bidder(bid, project_id) - }, - _ => { - log::error!("Project status not allowed for settlement: {:?}", project_details.status); - Ok(()) - } - }; - } - - let mut contribution_iter = Contributions::::iter_prefix((project_id,)); - - while let Some((_, contribution)) = contribution_iter.next() { - if remaining_weight.any_lt(at_least) { - return Weight::zero(); - } - let _ = match project_details.status { - ProjectStatus::FundingSuccessful => { - Self::do_settlement_success_contributor(contribution, project_id) - }, - ProjectStatus::FundingFailed => { - Self::do_settlement_failure_contributor(contribution, project_id) - }, - _ => { - log::error!("Project status not allowed for settlement: {:?}", project_details.status); - Ok(()) - } - }; - }; + ProjectSettlementQueue::::mutate(|queue| { queue.remove(0); From db58028ecc11246a53cf319c18c6c2c3a3a47f99 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Thu, 21 Mar 2024 14:47:54 +0100 Subject: [PATCH 06/21] add migrations and other things --- pallets/funding/src/functions.rs | 144 +++++++++------------------- pallets/funding/src/instantiator.rs | 66 +++++-------- pallets/funding/src/lib.rs | 99 ++++--------------- pallets/funding/src/mock.rs | 1 - pallets/funding/src/settlement.rs | 75 +++++++++------ pallets/funding/src/tests.rs | 36 ++----- 6 files changed, 145 insertions(+), 276 deletions(-) diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index b02df46c3..61be66655 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -30,7 +30,6 @@ use frame_support::{ }, }; use frame_system::pallet_prelude::BlockNumberFor; -use itertools::Itertools; use polimec_common::{ credentials::{Did, InvestorType}, ReleaseSchedule, @@ -306,7 +305,6 @@ impl Pallet { project_details.status = ProjectStatus::EvaluationFailed; project_details.cleanup = Cleaner::Failure(CleanerState::Initialized(PhantomData::)); ProjectsDetails::::insert(project_id, project_details); - ProjectSettlementQueue::::try_append(project_id).map_err(|_| Error::::TooManyProjectsInSettlementQueue)?; // * Emit events * Self::deposit_event(Event::EvaluationFailed { project_id }); @@ -860,14 +858,11 @@ impl Pallet { liquidity_pools_ct_amount, )?; - ProjectSettlementQueue::::try_append(project_id).map_err(|_| Error::::TooManyProjectsInSettlementQueue)?; - Ok(PostDispatchInfo { actual_weight: Some(WeightInfoOf::::start_settlement_funding_success()), pays_fee: Pays::Yes, }) } else { - ProjectSettlementQueue::::try_append(project_id).map_err(|_| Error::::TooManyProjectsInSettlementQueue)?; Ok(PostDispatchInfo { actual_weight: Some(WeightInfoOf::::start_settlement_funding_failure()), pays_fee: Pays::Yes, @@ -2412,9 +2407,6 @@ impl Pallet { // * Get variables * let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; let migration_readiness_check = project_details.migration_readiness_check.ok_or(Error::::NotAllowed)?; - let user_evaluations = Evaluations::::iter_prefix_values((project_id, participant.clone())); - let user_bids = Bids::::iter_prefix_values((project_id, participant.clone())); - let user_contributions = Contributions::::iter_prefix_values((project_id, participant.clone())); let project_para_id = project_details.parachain_id.ok_or(Error::::ImpossibleState)?; let now = >::block_number(); @@ -2422,16 +2414,9 @@ impl Pallet { ensure!(migration_readiness_check.is_ready(), Error::::NotAllowed); // * Process Data * - // u128 is a balance, u64 is now a BlockNumber, but will be a Moment/Timestamp in the future - let evaluation_migrations = - user_evaluations.filter_map(|evaluation| MigrationGenerator::::evaluation_migration(evaluation)); - let bid_migrations = user_bids.filter_map(|bid| MigrationGenerator::::bid_migration(bid)); - let contribution_migrations = - user_contributions.filter_map(|contribution| MigrationGenerator::::contribution_migration(contribution)); - - let migrations = evaluation_migrations.chain(bid_migrations).chain(contribution_migrations).collect_vec(); - let migrations = Migrations::from(migrations); + let migrations = MigrationQueue::::get(project_id, participant.clone()); + ensure!(!migrations.is_empty(), Error::::NoMigrationsFound); let constructed_migrations = Self::construct_migration_xcm_messages(migrations); for (migrations, xcm) in constructed_migrations { let project_multilocation = MultiLocation { parents: 1, interior: X1(Parachain(project_para_id.into())) }; @@ -2592,79 +2577,49 @@ impl Pallet { let project_account = Self::fund_account_id(project_id); let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PLMCPriceNotAvailable)?; - // Weight calculation variables - let mut accepted_bids_count = 0u32; - let mut rejected_bids_count = 0u32; + // sort bids by price, and equal prices sorted by id bids.sort_by(|a, b| b.cmp(a)); // accept only bids that were made before `end_block` i.e end of candle auction - let bids: Result, DispatchError> = bids + let (accepted_bids, rejected_bids): (Vec<_>, Vec<_>) = bids .into_iter() .map(|mut bid| { if bid.when > end_block { - rejected_bids_count += 1; - return Self::refund_bid(&mut bid, project_id, &project_account, RejectionReason::AfterCandleEnd) - .and(Ok(bid)); + bid.status = BidStatus::Rejected(RejectionReason::AfterCandleEnd); + return bid; } let buyable_amount = auction_allocation_size.saturating_sub(bid_token_amount_sum); if buyable_amount.is_zero() { - rejected_bids_count += 1; - return Self::refund_bid(&mut bid, project_id, &project_account, RejectionReason::NoTokensLeft) - .and(Ok(bid)); + bid.status = BidStatus::Rejected(RejectionReason::NoTokensLeft); } else if bid.original_ct_amount <= buyable_amount { - accepted_bids_count += 1; let ticket_size = bid.original_ct_usd_price.saturating_mul_int(bid.original_ct_amount); bid_token_amount_sum.saturating_accrue(bid.original_ct_amount); bid_usd_value_sum.saturating_accrue(ticket_size); + bid.final_ct_amount = bid.original_ct_amount; bid.status = BidStatus::Accepted; } else { - accepted_bids_count += 1; let ticket_size = bid.original_ct_usd_price.saturating_mul_int(buyable_amount); bid_usd_value_sum.saturating_accrue(ticket_size); bid_token_amount_sum.saturating_accrue(buyable_amount); bid.status = BidStatus::PartiallyAccepted(buyable_amount, RejectionReason::NoTokensLeft); bid.final_ct_amount = buyable_amount; - - let funding_asset_price = T::PriceProvider::get_price(bid.funding_asset.to_assethub_id()) - .ok_or(Error::::PriceNotFound)?; - let funding_asset_amount_needed = funding_asset_price - .reciprocal() - .ok_or(Error::::BadMath)? - .checked_mul_int(ticket_size) - .ok_or(Error::::BadMath)?; - T::FundingCurrency::transfer( - bid.funding_asset.to_assethub_id(), - &project_account, - &bid.bidder, - bid.funding_asset_amount_locked.saturating_sub(funding_asset_amount_needed), - Preservation::Preserve, - )?; - - let usd_bond_needed = bid - .multiplier - .calculate_bonding_requirement::(ticket_size) - .map_err(|_| Error::::BadMath)?; - let plmc_bond_needed = plmc_price - .reciprocal() - .ok_or(Error::::BadMath)? - .checked_mul_int(usd_bond_needed) - .ok_or(Error::::BadMath)?; - T::NativeCurrency::release( - &HoldReason::Participation(project_id.into()).into(), - &bid.bidder, - bid.plmc_bond.saturating_sub(plmc_bond_needed), - Precision::Exact, - )?; - - bid.funding_asset_amount_locked = funding_asset_amount_needed; - bid.plmc_bond = plmc_bond_needed; } - - Ok(bid) + bid }) - .collect(); - let bids = bids?; + .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. + for bid in rejected_bids.into_iter() { + Self::refund_bid(&bid, project_id, &project_account)?; + Bids::::remove((project_id, &bid.bidder, &bid.id)); + } + // 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 @@ -2683,36 +2638,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... + ensure!(!accepted_bids.is_empty(), Error::::NoBidsFound); let current_bucket = Buckets::::get(project_id).ok_or(Error::::ProjectNotFound)?; let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; let is_first_bucket = current_bucket.current_price == project_metadata.minimum_price; - let calc_weighted_price_fn = |bid: &BidInfoOf, amount: BalanceOf| -> Option> { - let ticket_size = bid.original_ct_usd_price.saturating_mul_int(amount); + 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); - Some(weighted_price) + weighted_price }; - let weighted_token_price = match is_first_bucket && !bids.is_empty() { - true => project_metadata.minimum_price, - false => bids - .iter() - .filter_map(|bid| match bid.status { - BidStatus::Accepted => calc_weighted_price_fn(bid, bid.original_ct_amount), - BidStatus::PartiallyAccepted(amount, _) => calc_weighted_price_fn(bid, amount), - _ => None, - }) - .reduce(|a, b| a.saturating_add(b)) - .ok_or(Error::::NoBidsFound)?, + let weighted_token_price = if is_first_bucket { + 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)) }; let mut final_total_funding_reached_by_bids = BalanceOf::::zero(); + + // Update storage // Update the bid in the storage - for mut bid in bids.into_iter() { - if bid.final_ct_usd_price > weighted_token_price { - bid.final_ct_usd_price = weighted_token_price; + for mut bid in accepted_bids.into_iter() { + 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; + } + let new_ticket_size = - weighted_token_price.checked_mul_int(bid.final_ct_amount).ok_or(Error::::BadMath)?; + bid.final_ct_usd_price.checked_mul_int(bid.final_ct_amount).ok_or(Error::::BadMath)?; let funding_asset_price = T::PriceProvider::get_price(bid.funding_asset.to_assethub_id()).ok_or(Error::::PriceNotFound)?; @@ -2757,7 +2714,6 @@ impl Pallet { Bids::::insert((project_id, &bid.bidder, &bid.id), &bid); } - // Update storage ProjectsDetails::::mutate(project_id, |maybe_info| -> DispatchResult { if let Some(info) = maybe_info { info.weighted_average_price = Some(weighted_token_price); @@ -2773,15 +2729,11 @@ impl Pallet { } /// Refund a bid because of `reason`. - fn refund_bid<'a>( - bid: &'a mut BidInfoOf, + fn refund_bid<>( + bid: &BidInfoOf, project_id: ProjectId, - project_account: &'a AccountIdOf, - reason: RejectionReason, + project_account: &AccountIdOf, ) -> Result<(), DispatchError> { - bid.status = BidStatus::Rejected(reason); - bid.final_ct_amount = Zero::zero(); - bid.final_ct_usd_price = Zero::zero(); T::FundingCurrency::transfer( bid.funding_asset.to_assethub_id(), @@ -2796,8 +2748,6 @@ impl Pallet { bid.plmc_bond, Precision::Exact, )?; - bid.funding_asset_amount_locked = Zero::zero(); - bid.plmc_bond = Zero::zero(); Ok(()) } @@ -3085,7 +3035,7 @@ impl Pallet { available_bytes_for_migration_per_message.saturating_div(one_migration_bytes) } - pub fn construct_migration_xcm_messages(migrations: Migrations) -> Vec<(Migrations, Xcm<()>)> { + pub fn construct_migration_xcm_messages(migrations: BoundedVec>) -> Vec<(Migrations, Xcm<()>)> { // TODO: adjust this as benchmarks for polimec-receiver are written const MAX_WEIGHT: Weight = Weight::from_parts(10_000, 0); @@ -3094,7 +3044,7 @@ impl Pallet { // TODO: use the actual pallet index when the fields are not private anymore (https://github.com/paritytech/polkadot-sdk/pull/2231) let mut output = Vec::new(); - for migrations_slice in migrations.inner().chunks(MaxMigrationsPerXcm::::get() as usize) { + for migrations_slice in migrations.chunks(MaxMigrationsPerXcm::::get() as usize) { let migrations_vec = migrations_slice.to_vec(); let migrations_item = Migrations::from(migrations_vec); diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index bbd10c183..2b2f09a85 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1010,15 +1010,6 @@ impl< self.execute(|| ProjectsDetails::::get(project_id).expect("Project details exists")) } - pub fn get_first_settlement_queue_item(&mut self) -> Option{ - self.execute(|| { - match ProjectSettlementQueue::::get().first() { - Some(project_id) => Some(*project_id), - None => None, - } - }) - } - pub fn get_update_block(&mut self, project_id: ProjectId, update_type: &UpdateType) -> Option> { self.execute(|| { ProjectsToUpdate::::iter().find_map(|(block, update_vec)| { @@ -1346,48 +1337,43 @@ impl< pub fn settle_project(&mut self, project_id: ProjectId) -> Result<(), DispatchError> { let details = self.get_project_details(project_id); - match details.status { - ProjectStatus::FundingSuccessful => self.settle_successfull_project(details), - ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => self.settle_failed_project(details), - _ => panic!("Project should be in FundingSuccessful, FundingFailed or EvaluationFailed status"), - }; - self. + self.execute(||{ + match details.status { + ProjectStatus::FundingSuccessful => Self::settle_successfull_project(project_id), + ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => Self::settle_failed_project(project_id, details.status), + _ => panic!("Project should be in FundingSuccessful, FundingFailed or EvaluationFailed status"), + } + }) } - pub fn assert_project_settled(&mut self, project_id: ProjectId) { - assert_eq!(Evaluations::::iter_prefix_keys((project_id,)).count(), 0); - assert_eq!(Bids::::iter_prefix_keys((project_id,)).count(), 0); - assert_eq!(Contributions::::iter_prefix_keys((project_id,)).count(), 0); - } + fn settle_successfull_project(project_id: ProjectId) -> Result<(), DispatchError> { + Evaluations::::iter_prefix((project_id,)).try_for_each(|(_, evaluation)| { + Pallet::::do_settlement_success_evaluator(evaluation, project_id) + })?; - fn settle_successfull_project(&mut self, project_id: ProjectId) -> Result<(), DispatchError> { - Evaluations::::iter_prefix((project_id,)).for_each(|(_, evaluation)| { - Pallet::::do_settlement_success_evaluator(evaluation, project_id); - }); + Bids::::iter_prefix((project_id,)).try_for_each(|(_, bid)| { + Pallet::::do_settlement_success_bidder(bid, project_id) + })?; - Bids::::iter_prefix((project_id,)).for_each(|(_, bid)| { - Pallet::::do_settlement_success_bidder(bid, project_id); - }); - - Contributions::::iter_prefix((project_id,)).for_each(|(_, contribution)| { - Pallet::::do_settlement_success_contributor(contribution, project_id); - }); + Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { + Pallet::::do_settlement_success_contributor(contribution, project_id) + }) } - fn settle_failed_project(&mut self, details: ProjectDetailsOf) -> Result<(), DispatchError> { - Evaluations::::iter_prefix((project_id,)).for_each(|(_, evaluation)| { + fn settle_failed_project(project_id: ProjectId, status: ProjectStatus) -> Result<(), DispatchError> { + Evaluations::::iter_prefix((project_id,)).try_for_each(|(_, evaluation)| { Pallet::::do_settlement_failure_evaluator(evaluation, project_id) - }); - - if details.status == ProjectStatus::FundingFailed { - Bids::::iter_prefix((project_id,)).for_each(|(_, bid)| { + })?; + if status == ProjectStatus::FundingFailed { + Bids::::iter_prefix((project_id,)).try_for_each(|(_, bid)| { Pallet::::do_settlement_failure_bidder(bid, project_id) - }); + })?; - Contributions::::iter_prefix((project_id,)).for_each(|(_, contribution)| { + Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { Pallet::::do_settlement_failure_contributor(contribution, project_id) - }); + })?; } + Ok(()) } pub fn create_remainder_contributing_project( diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 6e4737f32..1096c6e95 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -134,10 +134,8 @@ use polkadot_parachain::primitives::Id as ParaId; use sp_arithmetic::traits::{One, Saturating}; use sp_runtime::{traits::AccountIdConversion, FixedPointNumber, FixedPointOperand, FixedU128}; use sp_std::{marker::PhantomData, prelude::*}; -use traits::DoRemainingOperation; pub use types::*; use xcm::v3::{opaque::Instruction, prelude::*, SendXcm}; -use parachains_common::MAXIMUM_BLOCK_WEIGHT; pub mod functions; pub mod settlement; @@ -334,10 +332,6 @@ pub mod pallet { #[pallet::constant] type MaxMessageSizeThresholds: Get<(u32, u32)>; - /// Max Projects to add to settlement queue. - #[pallet::constant] - type MaxProjectsToSettle: Get; - /// max iterations for trying to insert a project on the projects_to_update storage #[pallet::constant] type MaxProjectsToUpdateInsertionAttempts: Get; @@ -555,7 +549,23 @@ pub mod pallet { >; #[pallet::storage] - pub type ProjectSettlementQueue = StorageValue<_, BoundedVec, ValueQuery>; + pub type MigrationQueue = StorageDoubleMap< + _, + Blake2_128Concat, + ProjectId, + Blake2_128Concat, + T::AccountId, + BoundedVec>, + ValueQuery, + >; + + + pub struct MaxParticipationsPerUser(PhantomData); + impl Get for MaxParticipationsPerUser { + fn get() -> u32 { + T::MaxContributionsPerUser::get() + T::MaxBidsPerUser::get() + T::MaxEvaluationsPerUser::get() + } + } #[pallet::storage] /// Migrations sent and awaiting for confirmation @@ -967,8 +977,10 @@ pub mod pallet { TooManyEvaluationsForProject, /// Reached contribution limit for this user on this project TooManyContributionsForUser, - /// Reached the settlement queue limit. - TooManyProjectsInSettlementQueue, + /// Reached the migration queue limit for a user. + TooManyMigrations, + /// User has no migrations to execute. + NoMigrationsFound, // Participant tried to do a community contribution but it already had a winning bid on the auction round. UserHasWinningBids, // Round transition already happened. @@ -1615,75 +1627,6 @@ pub mod pallet { used_weight } - - fn on_idle(_now: BlockNumberFor, max_weight: Weight) -> Weight { - let mut remaining_weight = max_weight; - - // We want at least 5% of max block weight - let at_least = MAXIMUM_BLOCK_WEIGHT.saturating_div(20); - if remaining_weight.any_lt(at_least) { - return Weight::zero(); - }; - let project_ids: BoundedVec = ProjectSettlementQueue::::get(); - if let Some(&project_id) = project_ids.first() { - if let Some(project_details) = ProjectsDetails::::get(project_id) { - - - ProjectSettlementQueue::::mutate(|queue| { - queue.remove(0); - }); - - } else { - log::error!("Project details not found for project_id: {:?} in ProjectSettlementQueue", project_id); - } - }; - - - remaining_weight - - // let projects_needing_cleanup = ProjectsDetails::::iter() - // .filter_map(|(project_id, info)| match info.cleanup { - // cleaner if >::has_remaining_operations(&cleaner) => - // Some((project_id, cleaner)), - // _ => None, - // }) - // .collect::>(); - - // let projects_amount = projects_needing_cleanup.len() as u64; - // if projects_amount == 0 { - // return max_weight; - // } - - // let mut max_weight_per_project = remaining_weight.saturating_div(projects_amount); - - // for (remaining_projects, (project_id, mut cleaner)) in - // projects_needing_cleanup.into_iter().enumerate().rev() - // { - // // TODO: Create this benchmark - // // let mut consumed_weight = WeightInfoOf::::insert_cleaned_project(); - // let mut consumed_weight = Weight::from_parts(6_034_000, 0); - // while !consumed_weight.any_gt(max_weight_per_project) { - // if let Ok(weight) = >::do_one_operation(&mut cleaner, project_id) - // { - // consumed_weight.saturating_accrue(weight); - // } else { - // break; - // } - // } - - // let mut details = - // if let Some(details) = ProjectsDetails::::get(project_id) { details } else { continue }; - // details.cleanup = cleaner; - // ProjectsDetails::::insert(project_id, details); - - // remaining_weight = remaining_weight.saturating_sub(consumed_weight); - // if remaining_projects > 0 { - // max_weight_per_project = remaining_weight.saturating_div(remaining_projects as u64); - // } - // } - - // max_weight.saturating_sub(remaining_weight) - } } #[pallet::genesis_config] diff --git a/pallets/funding/src/mock.rs b/pallets/funding/src/mock.rs index 4f3b1c669..efc5f8295 100644 --- a/pallets/funding/src/mock.rs +++ b/pallets/funding/src/mock.rs @@ -370,7 +370,6 @@ impl Config for TestRuntime { type MaxEvaluationsPerProject = ConstU32<1024>; type MaxEvaluationsPerUser = ConstU32<4>; type MaxMessageSizeThresholds = MaxMessageSizeThresholds; - type MaxProjectsToSettle = ConstU32<100>; type MaxProjectsToUpdateInsertionAttempts = ConstU32<100>; type MaxProjectsToUpdatePerBlock = ConstU32<1>; type Multiplier = Multiplier; diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index 0c0d25d82..8101ba880 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -1,11 +1,11 @@ use super::*; - +use crate::traits::VestingDurationCalculation; use frame_support::{ - dispatch::{ DispatchResult}, + dispatch::DispatchResult, ensure, pallet_prelude::*, traits::{ - fungible::{InspectHold, MutateHold as FungibleMutateHold}, + fungible::{ MutateHold as FungibleMutateHold}, fungibles::{ Inspect, Mutate as FungiblesMutate, }, @@ -13,9 +13,9 @@ use frame_support::{ Get, }, }; -use sp_runtime::{traits::Zero, Perquintill}; +use sp_runtime::{Perquintill, traits::{Zero, Convert}}; use polimec_common::{ - migration_types::{MigrationInfo, MigrationOrigin, Migrations, ParticipationType}, + migration_types::{MigrationInfo, MigrationOrigin, ParticipationType}, ReleaseSchedule, }; @@ -52,6 +52,7 @@ impl Pallet { // Payout the bid funding asset amount to the project account Self::release_funding_asset(project_id, &project_details.issuer_account, bid.funding_asset_amount_locked, bid.funding_asset)?; + Self::create_migration(project_id, &bidder, bid.id, ParticipationType::Bid, bid.final_ct_amount, vest_info.duration.into())?; // TODO: Create MigrationInfo Bids::::remove((project_id, bidder, bid.id)); @@ -70,8 +71,6 @@ impl Pallet { let bidder = bid.bidder; - // Release the held future ct deposit - Self::release_future_ct_deposit(project_id, &bidder)?; if matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) { // Return the funding assets to the bidder @@ -118,6 +117,9 @@ impl Pallet { // Payout the bid funding asset amount to the project account Self::release_funding_asset(project_id, &project_details.issuer_account, contribution.funding_asset_amount, contribution.funding_asset)?; + // Create Migration + Self::create_migration(project_id, &contributor, contribution.id, ParticipationType::Contribution, contribution.ct_amount, vest_info.duration.into())?; + Contributions::::remove((project_id, contributor, contribution.id)); Ok(()) @@ -133,8 +135,6 @@ impl Pallet { // Check if the bidder has a future deposit held let contributor = contribution.contributor; - // Release the held future ct deposit - Self::release_future_ct_deposit(project_id, &contributor)?; // Return the funding assets to the contributor Self::release_funding_asset(project_id, &contributor, contribution.funding_asset_amount, contribution.funding_asset)?; @@ -159,10 +159,10 @@ impl Pallet { // 1. Slashed // 2. Rewarded with CT tokens // 3. Not slashed or Rewarded. - let bond = match project_details.evaluation_round_info.evaluators_outcome { - EvaluatorsOutcome::Slashed => Self::slash_evaluator(project_id, &evaluation)?, + let (bond, reward): (BalanceOf, BalanceOf) = match project_details.evaluation_round_info.evaluators_outcome { + EvaluatorsOutcome::Slashed => (Self::slash_evaluator(project_id, &evaluation)?, Zero::zero()), EvaluatorsOutcome::Rewarded(info) => Self::reward_evaluator(project_id, &evaluation, &info)?, - EvaluatorsOutcome::Unchanged => evaluation.current_plmc_bond, + EvaluatorsOutcome::Unchanged => (evaluation.current_plmc_bond, Zero::zero()), }; // Release the held PLMC bond @@ -173,6 +173,12 @@ impl Pallet { Precision::Exact, )?; + // Create Migration + if reward > Zero::zero() { + let multiplier = MultiplierOf::::try_from(1u8).map_err(|_| Error::::BadMath)?; + let duration = multiplier.calculate_vesting_duration::(); + Self::create_migration(project_id, &evaluation.evaluator, evaluation.id, ParticipationType::Evaluation, reward, duration)?; + } Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); // TODO: Emit an event @@ -194,8 +200,6 @@ impl Pallet { bond = evaluation.current_plmc_bond; } - // Release the held future ct deposit - Self::release_future_ct_deposit(project_id, &evaluation.evaluator)?; // Release the held PLMC bond T::NativeCurrency::release( @@ -214,27 +218,12 @@ impl Pallet { fn mint_ct_tokens(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { if !T::ContributionTokenCurrency::contains(&project_id, participant) { - Self::release_future_ct_deposit(project_id, participant)?; T::ContributionTokenCurrency::touch(project_id, participant.clone(), participant.clone())?; } T::ContributionTokenCurrency::mint_into(project_id, participant, amount)?; Ok(()) } - fn release_future_ct_deposit(project_id: ProjectId, participant: &AccountIdOf) -> DispatchResult { - let held_plmc = T::NativeCurrency::balance_on_hold(&HoldReason::FutureDeposit(project_id).into(), participant); - ensure!(held_plmc > Zero::zero(), Error::::NoFutureDepositHeld); - - // Return the held deposit to the bidder - T::NativeCurrency::release( - &HoldReason::FutureDeposit(project_id).into(), - participant, - T::ContributionTokenCurrency::deposit_required(project_id), - Precision::Exact, - )?; - Ok(()) - } - fn release_funding_asset(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf, asset: AcceptedFundingAsset) -> DispatchResult { let project_pot = Self::fund_account_id(project_id); T::FundingCurrency::transfer( @@ -280,13 +269,13 @@ impl Pallet { Ok(evaluation.current_plmc_bond.saturating_sub(slashed_amount)) } - fn reward_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf, info: &RewardInfoOf) -> Result, DispatchError> { + fn reward_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf, info: &RewardInfoOf) -> Result<(BalanceOf, BalanceOf), DispatchError> { let reward = Self::calculate_evaluator_reward(evaluation, &info); Self::mint_ct_tokens(project_id, &evaluation.evaluator, reward)?; - Ok(evaluation.current_plmc_bond) + Ok((evaluation.current_plmc_bond, reward)) } pub fn calculate_evaluator_reward(evaluation: &EvaluationInfoOf, info: &RewardInfoOf) -> BalanceOf { @@ -303,4 +292,28 @@ impl Pallet { let total_reward_amount = early_evaluators_rewards.saturating_add(normal_evaluators_rewards); total_reward_amount } + + pub fn create_migration( + project_id: ProjectId, + origin: &AccountIdOf, + id: u32, + participation_type: ParticipationType, + ct_amount: BalanceOf, + vesting_time: BlockNumberFor, + ) -> DispatchResult { + MigrationQueue::::try_mutate(project_id, origin, |migrations| -> DispatchResult { + let migration_origin = MigrationOrigin { + user: T::AccountId32Conversion::convert(origin.clone()), + id: id, + participation_type, + }; + let vesting_time: u64 = vesting_time.try_into().map_err(|_| Error::::BadMath)?; + let migration_info: MigrationInfo = (ct_amount.into(), vesting_time.into()).into(); + let migration = Migration::new(migration_origin, migration_info); + migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; + Ok(()) + })?; + + Ok(()) + } } \ No newline at end of file diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index f2f004607..752875109 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -664,7 +664,7 @@ mod evaluation { inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.advance_time(10).unwrap(); + inst.settle_project(project_id).unwrap(); let actual_reward_balances = inst.execute(|| { vec![ @@ -721,8 +721,7 @@ mod evaluation { assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationFailed); - // Check that on_idle has unlocked the failed bonds - inst.advance_time(10).unwrap(); + inst.settle_project(project_id).unwrap(); inst.do_free_plmc_assertions(expected_evaluator_balances); } @@ -1062,18 +1061,7 @@ mod auction { } for bid in excluded_bids { - let mut stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id, bid.bidder))); - let desired_bid: BidInfoFilter = BidInfoFilter { - project_id: Some(project_id), - bidder: Some(bid.bidder), - original_ct_amount: Some(bid.amount), - status: Some(BidStatus::Rejected(RejectionReason::AfterCandleEnd)), - ..Default::default() - }; - assert!( - inst.execute(|| stored_bids.any(|bid| desired_bid.matches_bid(&bid))), - "Stored bid does not match the given filter" - ); + assert!(inst.execute(|| Bids::::iter_prefix_values((project_id, bid.bidder)).count() == 0)); } } @@ -1238,9 +1226,7 @@ mod auction { inst.finish_funding(project_id).unwrap(); inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_eq!(inst.get_first_settlement_queue_item(), Some(project_id)); - inst.advance_time(10u64).unwrap(); - assert_eq!(inst.get_first_settlement_queue_item(), None); + inst.settle_project(project_id).unwrap(); let plmc_locked_for_accepted_bid = MockInstantiator::calculate_auction_plmc_charged_with_given_price(&accepted_bid, final_price); @@ -3758,17 +3744,9 @@ mod funding_end { assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_matches!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); inst.test_ct_created_for(project_id); - inst.advance_time(10u64).unwrap(); - assert_matches!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Finished(PhantomData)) - ); + inst.settle_project(project_id).unwrap(); } #[test] @@ -3972,8 +3950,8 @@ mod funding_end { ); inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_eq!(inst.execute(|| {ProjectSettlementQueue::::get().len()}), 1); - inst.advance_time(10u64).unwrap(); + + inst.settle_project(project_id).unwrap(); for (account, amount) in all_ct_expectations { let minted = From fca2835fc6c42336d2d565b43d56ca85647a130c Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Thu, 21 Mar 2024 16:25:12 +0100 Subject: [PATCH 07/21] make all non funding tests + benchmarks pass + change banchmarks --- pallets/funding/src/benchmarking.rs | 1215 ++------ pallets/funding/src/functions.rs | 641 ---- pallets/funding/src/impls.rs | 1412 ++++----- pallets/funding/src/instantiator.rs | 16 +- pallets/funding/src/lib.rs | 180 +- pallets/funding/src/mock.rs | 2 +- pallets/funding/src/settlement.rs | 137 +- pallets/funding/src/tests.rs | 4218 +++++++++++++-------------- pallets/funding/src/weights.rs | 587 +--- 9 files changed, 3173 insertions(+), 5235 deletions(-) diff --git a/pallets/funding/src/benchmarking.rs b/pallets/funding/src/benchmarking.rs index d92b4c511..77dd587c1 100644 --- a/pallets/funding/src/benchmarking.rs +++ b/pallets/funding/src/benchmarking.rs @@ -1347,797 +1347,6 @@ mod benchmarks { ); } - #[benchmark] - fn evaluation_unbond_for() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let evaluations = default_evaluations::(); - let evaluator = evaluations[0].account.clone(); - whitelist_account!(evaluator); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - evaluations, - default_bids::(), - default_community_contributions::(), - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let evaluation_to_unbond = - inst.execute(|| Evaluations::::iter_prefix_values((project_id, evaluator.clone())).next().unwrap()); - - inst.execute(|| { - PalletFunding::::evaluation_reward_payout_for( - ::RuntimeOrigin::signed(evaluator.clone().into()), - project_id, - evaluator.clone(), - evaluation_to_unbond.id, - ) - .expect("") - }); - - #[extrinsic_call] - evaluation_unbond_for( - RawOrigin::Signed(evaluator.clone()), - project_id, - evaluator.clone(), - evaluation_to_unbond.id, - ); - - // * validity checks * - // Balance - let bonded_plmc = inst - .get_reserved_plmc_balances_for(vec![evaluator.clone()], HoldReason::Evaluation(project_id).into())[0] - .plmc_amount; - assert_eq!(bonded_plmc, 0.into()); - - // Events - frame_system::Pallet::::assert_last_event( - Event::BondReleased { - project_id, - amount: evaluation_to_unbond.current_plmc_bond, - bonder: evaluator.clone(), - releaser: evaluator, - } - .into(), - ); - } - - #[benchmark] - fn evaluation_reward_payout_for_with_ct_account_creation() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let evaluations: Vec> = default_evaluations::(); - let evaluator: AccountIdOf = evaluations[0].account.clone(); - whitelist_account!(evaluator); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - evaluations, - default_bids::(), - default_community_contributions::(), - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let evaluation_to_unbond = - inst.execute(|| Evaluations::::iter_prefix_values((project_id, evaluator.clone())).next().unwrap()); - - #[extrinsic_call] - evaluation_reward_payout_for( - RawOrigin::Signed(evaluator.clone()), - project_id, - evaluator.clone(), - evaluation_to_unbond.id, - ); - - // * validity checks * - // Storage - let stored_evaluation = - Evaluations::::get((project_id, evaluator.clone(), evaluation_to_unbond.id)).unwrap(); - assert!(stored_evaluation.rewarded_or_slashed.is_some()); - - // Balances - let project_details = ProjectsDetails::::get(project_id).unwrap(); - let reward_info = match project_details.evaluation_round_info.evaluators_outcome { - EvaluatorsOutcome::Rewarded(reward_info) => reward_info, - _ => panic!("EvaluatorsOutcome should be Rewarded"), - }; - let total_reward = - BenchInstantiator::::calculate_total_reward_for_evaluation(stored_evaluation.clone(), reward_info); - let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![evaluator.clone()])[0]; - assert_eq!(ct_amount, total_reward); - - // Events - frame_system::Pallet::::assert_last_event( - Event::EvaluationRewarded { - project_id, - evaluator: evaluator.clone(), - id: stored_evaluation.id, - amount: total_reward, - caller: evaluator, - } - .into(), - ); - } - - #[benchmark] - fn evaluation_reward_payout_for_no_ct_account_creation() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let mut evaluations = default_evaluations::(); - let evaluator: AccountIdOf = evaluations[0].account.clone(); - evaluations[1].account = evaluator.clone(); - whitelist_account!(evaluator); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - evaluations, - default_bids::(), - default_community_contributions::(), - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let mut evaluations_to_unbond = - inst.execute(|| Evaluations::::iter_prefix_values((project_id, evaluator.clone()))); - - let pre_evaluation = evaluations_to_unbond.next().unwrap(); - let bench_evaluation = evaluations_to_unbond.next().unwrap(); - - Pallet::::evaluation_reward_payout_for( - RawOrigin::Signed(evaluator.clone()).into(), - project_id, - evaluator.clone(), - pre_evaluation.id, - ) - .unwrap(); - - #[extrinsic_call] - evaluation_reward_payout_for( - RawOrigin::Signed(evaluator.clone()), - project_id, - evaluator.clone(), - bench_evaluation.id, - ); - - // * validity checks * - // Storage - let stored_evaluation = Evaluations::::get((project_id, evaluator.clone(), bench_evaluation.id)).unwrap(); - assert!(stored_evaluation.rewarded_or_slashed.is_some()); - - // Balances - let project_details = ProjectsDetails::::get(project_id).unwrap(); - let reward_info = match project_details.evaluation_round_info.evaluators_outcome { - EvaluatorsOutcome::Rewarded(reward_info) => reward_info, - _ => panic!("EvaluatorsOutcome should be Rewarded"), - }; - - let pre_reward = - BenchInstantiator::::calculate_total_reward_for_evaluation(pre_evaluation.clone(), reward_info.clone()); - let bench_reward = - BenchInstantiator::::calculate_total_reward_for_evaluation(bench_evaluation.clone(), reward_info); - let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![evaluator.clone()])[0]; - assert_eq!(ct_amount, pre_reward + bench_reward); - - // Events - frame_system::Pallet::::assert_last_event( - Event::EvaluationRewarded { - project_id, - evaluator: evaluator.clone(), - id: stored_evaluation.id, - amount: bench_reward, - caller: evaluator, - } - .into(), - ); - } - - #[benchmark] - fn evaluation_slash_for() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let evaluations = default_evaluations::(); - let evaluator = evaluations[0].account.clone(); - whitelist_account!(evaluator); - - let project_metadata = default_project::(inst.get_new_nonce(), issuer.clone()); - let target_funding_amount: BalanceOf = - project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size); - - let bids = BenchInstantiator::generate_bids_from_total_usd( - Percent::from_percent(15) * target_funding_amount, - 10u128.into(), - default_weights(), - default_bidders::(), - default_bidder_multipliers(), - ); - let contributions = BenchInstantiator::generate_contributions_from_total_usd( - Percent::from_percent(10) * target_funding_amount, - project_metadata.minimum_price, - default_weights(), - default_community_contributors::(), - default_community_contributor_multipliers(), - ); - - let project_id = - inst.create_finished_project(project_metadata, issuer, evaluations, bids, contributions, vec![]); - - inst.advance_time(One::one()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Initialized(PhantomData)) - ); - - let evaluation_to_unbond = - inst.execute(|| Evaluations::::iter_prefix_values((project_id, evaluator.clone())).next().unwrap()); - - #[extrinsic_call] - evaluation_slash_for( - RawOrigin::Signed(evaluator.clone()), - project_id, - evaluator.clone(), - evaluation_to_unbond.id, - ); - - // * validity checks * - // Storage - let stored_evaluation = - Evaluations::::get((project_id, evaluator.clone(), evaluation_to_unbond.id)).unwrap(); - assert!(stored_evaluation.rewarded_or_slashed.is_some()); - let slashed_amount = T::EvaluatorSlash::get() * evaluation_to_unbond.original_plmc_bond; - let current_plmc_bond = evaluation_to_unbond.current_plmc_bond.saturating_sub(slashed_amount); - assert_eq!(stored_evaluation.current_plmc_bond, current_plmc_bond); - - // Balance - let treasury_account = T::ProtocolGrowthTreasury::get(); - let bonded_plmc = inst - .get_reserved_plmc_balances_for(vec![evaluator.clone()], HoldReason::Evaluation(project_id).into())[0] - .plmc_amount; - assert_eq!(bonded_plmc, stored_evaluation.current_plmc_bond); - let free_treasury_plmc = inst.get_free_plmc_balances_for(vec![treasury_account])[0].plmc_amount; - let ed = BenchInstantiator::::get_ed(); - assert_eq!(free_treasury_plmc, slashed_amount + ed); - - // Events - frame_system::Pallet::::assert_last_event( - Event::EvaluationSlashed { - project_id, - evaluator: evaluator.clone(), - id: stored_evaluation.id, - amount: slashed_amount, - caller: evaluator, - } - .into(), - ); - } - - #[benchmark] - fn bid_ct_mint_for_with_ct_account_creation() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let bids = default_bids::(); - let bidder = bids[0].bidder.clone(); - whitelist_account!(bidder); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - default_evaluations::(), - bids, - default_community_contributions::(), - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let bid_to_mint_ct = - inst.execute(|| Bids::::iter_prefix_values((project_id, bidder.clone())).next().unwrap()); - - #[extrinsic_call] - bid_ct_mint_for(RawOrigin::Signed(bidder.clone()), project_id, bidder.clone(), bid_to_mint_ct.id); - - // * validity checks * - // Storage - let stored_bid = Bids::::get((project_id, bidder.clone(), bid_to_mint_ct.id)).unwrap(); - assert!(stored_bid.ct_minted); - - // Balances - let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![bidder.clone()])[0]; - assert_eq!(stored_bid.final_ct_amount, ct_amount); - - // Events - frame_system::Pallet::::assert_last_event( - Event::ContributionTokenMinted { releaser: bidder.clone(), project_id, claimer: bidder, amount: ct_amount } - .into(), - ); - } - - #[benchmark] - fn bid_ct_mint_for_no_ct_account_creation() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let bids: Vec> = default_bids::(); - let bidder = bids[0].bidder.clone(); - whitelist_account!(bidder); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - default_evaluations::(), - bids, - default_community_contributions::(), - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let mut bids_to_mint_ct = inst.execute(|| Bids::::iter_prefix_values((project_id, bidder.clone()))); - - let bench_bid_to_mint_ct = bids_to_mint_ct.next().unwrap(); - - #[extrinsic_call] - bid_ct_mint_for(RawOrigin::Signed(bidder.clone()), project_id, bidder.clone(), bench_bid_to_mint_ct.id); - - // * validity checks * - // Storage - let stored_bid = Bids::::get((project_id, bidder.clone(), bench_bid_to_mint_ct.id)).unwrap(); - assert!(stored_bid.ct_minted); - - // Balances - let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![bidder.clone()])[0]; - assert_eq!(ct_amount, stored_bid.final_ct_amount); - - // Events - frame_system::Pallet::::assert_last_event( - Event::ContributionTokenMinted { - releaser: bidder.clone(), - project_id, - claimer: bidder, - amount: bench_bid_to_mint_ct.final_ct_amount, - } - .into(), - ); - } - - #[benchmark] - fn contribution_ct_mint_for_with_ct_account_creation() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let contributions = default_community_contributions::(); - let contributor = contributions[0].contributor.clone(); - whitelist_account!(contributor); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - default_evaluations::(), - default_bids::(), - contributions, - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let contribution_to_mint_ct = - inst.execute(|| Contributions::::iter_prefix_values((project_id, contributor.clone())).next().unwrap()); - - #[extrinsic_call] - contribution_ct_mint_for( - RawOrigin::Signed(contributor.clone()), - project_id, - contributor.clone(), - contribution_to_mint_ct.id, - ); - - // * validity checks * - // Storage - let stored_contribution = - Contributions::::get((project_id, contributor.clone(), contribution_to_mint_ct.id)).unwrap(); - assert!(stored_contribution.ct_minted); - - // Balances - let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![contributor.clone()])[0]; - assert_eq!(stored_contribution.ct_amount, ct_amount); - - // Events - frame_system::Pallet::::assert_last_event( - Event::ContributionTokenMinted { - releaser: contributor.clone(), - project_id, - claimer: contributor, - amount: ct_amount, - } - .into(), - ); - } - - #[benchmark] - fn contribution_ct_mint_for_no_ct_account_creation() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let mut contributions: Vec> = default_community_contributions::(); - let contributor = contributions[0].contributor.clone(); - contributions[1].contributor = contributor.clone(); - whitelist_account!(contributor); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - default_evaluations::(), - default_bids::(), - contributions, - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let mut contributions_to_mint_ct = - inst.execute(|| Contributions::::iter_prefix_values((project_id, contributor.clone()))); - - let pre_contribution_to_mint_ct = contributions_to_mint_ct.next().unwrap(); - let bench_contribution_to_mint_ct = contributions_to_mint_ct.next().unwrap(); - - Pallet::::contribution_ct_mint_for( - RawOrigin::Signed(contributor.clone()).into(), - project_id, - contributor.clone(), - pre_contribution_to_mint_ct.id, - ) - .unwrap(); - - #[extrinsic_call] - contribution_ct_mint_for( - RawOrigin::Signed(contributor.clone()), - project_id, - contributor.clone(), - bench_contribution_to_mint_ct.id, - ); - - // * validity checks * - // Storage - let stored_contribution = - Contributions::::get((project_id, contributor.clone(), bench_contribution_to_mint_ct.id)).unwrap(); - assert!(stored_contribution.ct_minted); - - // Balances - let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![contributor.clone()])[0]; - assert_eq!(ct_amount, pre_contribution_to_mint_ct.ct_amount + bench_contribution_to_mint_ct.ct_amount); - - // Events - frame_system::Pallet::::assert_last_event( - Event::ContributionTokenMinted { - releaser: contributor.clone(), - project_id, - claimer: contributor, - amount: bench_contribution_to_mint_ct.ct_amount, - } - .into(), - ); - } - - #[benchmark] - fn start_bid_vesting_schedule_for() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let bids = default_bids::(); - let bidder = bids[0].bidder.clone(); - whitelist_account!(bidder); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - default_evaluations::(), - bids, - default_community_contributions::(), - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let bid_to_vest = inst.execute(|| Bids::::iter_prefix_values((project_id, bidder.clone())).next().unwrap()); - - #[extrinsic_call] - start_bid_vesting_schedule_for(RawOrigin::Signed(bidder.clone()), project_id, bidder.clone(), bid_to_vest.id); - - // * validity checks * - // Storage - let stored_bid = Bids::::get((project_id, bidder.clone(), bid_to_vest.id)).unwrap(); - assert!(stored_bid.plmc_vesting_info.is_some()); - let vest_info = stored_bid.plmc_vesting_info.unwrap(); - let total_vested = - T::Vesting::total_scheduled_amount(&bidder, HoldReason::Participation(project_id).into()).unwrap(); - assert_eq!(vest_info.total_amount, total_vested); - - // Events - frame_system::Pallet::::assert_last_event( - Event::BidPlmcVestingScheduled { - project_id, - bidder: bidder.clone(), - id: stored_bid.id, - amount: vest_info.total_amount, - caller: bidder, - } - .into(), - ); - } - - #[benchmark] - fn start_contribution_vesting_schedule_for() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let contributions = default_community_contributions::(); - let contributor = contributions[0].contributor.clone(); - whitelist_account!(contributor); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - default_evaluations::(), - default_bids::(), - contributions, - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let contribution_to_vest = - inst.execute(|| Contributions::::iter_prefix_values((project_id, contributor.clone())).next().unwrap()); - - #[extrinsic_call] - start_contribution_vesting_schedule_for( - RawOrigin::Signed(contributor.clone()), - project_id, - contributor.clone(), - contribution_to_vest.id, - ); - - // * validity checks * - // Storage - let stored_contribution = - Contributions::::get((project_id, contributor.clone(), contribution_to_vest.id)).unwrap(); - assert!(stored_contribution.plmc_vesting_info.is_some()); - let vest_info = stored_contribution.plmc_vesting_info.unwrap(); - let total_vested = - T::Vesting::total_scheduled_amount(&contributor, HoldReason::Participation(project_id).into()).unwrap(); - assert_eq!(vest_info.total_amount, total_vested); - - // Events - frame_system::Pallet::::assert_last_event( - Event::ContributionPlmcVestingScheduled { - project_id, - contributor: contributor.clone(), - id: stored_contribution.id, - amount: vest_info.total_amount, - caller: contributor, - } - .into(), - ); - } - - #[benchmark] - fn payout_bid_funds_for() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let bids = default_bids::(); - let bidder = bids[0].bidder.clone(); - whitelist_account!(bidder); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer.clone(), - default_evaluations::(), - bids, - default_community_contributions::(), - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let bid_to_payout = - inst.execute(|| Bids::::iter_prefix_values((project_id, bidder.clone())).next().unwrap()); - - #[extrinsic_call] - payout_bid_funds_for(RawOrigin::Signed(issuer.clone()), project_id, bidder.clone(), bid_to_payout.id); - - // * validity checks * - // Storage - let stored_bid = Bids::::get((project_id, bidder.clone(), bid_to_payout.id)).unwrap(); - assert!(stored_bid.funds_released); - - // Balances - let asset = stored_bid.funding_asset.to_assethub_id(); - let project_details = ProjectsDetails::::get(project_id).unwrap(); - let free_assets = - inst.get_free_foreign_asset_balances_for(asset, vec![project_details.issuer_account])[0].asset_amount; - assert_eq!(free_assets, stored_bid.funding_asset_amount_locked); - - // Events - frame_system::Pallet::::assert_last_event( - Event::BidFundingPaidOut { project_id, bidder, id: stored_bid.id, amount: free_assets, caller: issuer } - .into(), - ); - } - - #[benchmark] - fn payout_contribution_funds_for() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let contributions = default_community_contributions::(); - let contributor = contributions[0].contributor.clone(); - whitelist_account!(contributor); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer.clone(), - default_evaluations::(), - default_bids::(), - contributions, - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let contribution_to_payout = - inst.execute(|| Contributions::::iter_prefix_values((project_id, contributor.clone())).next().unwrap()); - - #[extrinsic_call] - payout_contribution_funds_for( - RawOrigin::Signed(issuer.clone()), - project_id, - contributor.clone(), - contribution_to_payout.id, - ); - - // * validity checks * - // Storage - let stored_contribution = - Contributions::::get((project_id, contributor.clone(), contribution_to_payout.id)).unwrap(); - assert!(stored_contribution.funds_released); - - // Balances - let asset = stored_contribution.funding_asset.to_assethub_id(); - let project_details = ProjectsDetails::::get(project_id).unwrap(); - let free_assets = - inst.get_free_foreign_asset_balances_for(asset, vec![project_details.issuer_account])[0].asset_amount; - assert_eq!(free_assets, stored_contribution.funding_asset_amount); - - // Events - frame_system::Pallet::::assert_last_event( - Event::ContributionFundingPaidOut { - project_id, - contributor, - id: stored_contribution.id, - amount: free_assets, - caller: issuer, - } - .into(), - ); - } - #[benchmark] fn decide_project_outcome( // Insertion attempts in add_to_update_store. Total amount of storage items iterated through in `ProjectsToUpdate`. Leave one free to make the extrinsic pass @@ -2201,7 +1410,71 @@ mod benchmarks { } #[benchmark] - fn release_bid_funds_for() { + fn settle_successful_evaluation() { + // setup + let mut inst = BenchInstantiator::::new(None); + + // real benchmark starts at block 0, and we can't call `events()` at block 0 + inst.advance_time(1u32.into()).unwrap(); + + let issuer = account::>("issuer", 0, 0); + let evaluations: Vec> = default_evaluations::(); + let evaluator: AccountIdOf = evaluations[0].account.clone(); + whitelist_account!(evaluator); + + let project_id = inst.create_finished_project( + default_project::(inst.get_new_nonce(), issuer.clone()), + issuer, + evaluations, + default_bids::(), + default_community_contributions::(), + vec![], + ); + + run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); + + let evaluation_to_settle = + inst.execute(|| Evaluations::::iter_prefix_values((project_id, evaluator.clone())).next().unwrap()); + + #[extrinsic_call] + settle_successful_evaluation( + RawOrigin::Signed(evaluator.clone()), + project_id, + evaluator.clone(), + evaluation_to_settle.id, + ); + + // * validity checks * + // Evaluation should be removed + assert!(Evaluations::::get((project_id, evaluator.clone(), evaluation_to_settle.id)).is_none()); + + + // Balances + let project_details = ProjectsDetails::::get(project_id).unwrap(); + let reward_info = match project_details.evaluation_round_info.evaluators_outcome { + EvaluatorsOutcome::Rewarded(reward_info) => reward_info, + _ => panic!("EvaluatorsOutcome should be Rewarded"), + }; + let reward = Pallet::::calculate_evaluator_reward(&evaluation_to_settle, &reward_info); + + let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![evaluator.clone()])[0]; + assert_eq!(ct_amount, reward); + + // Events + // frame_system::Pallet::::assert_last_event( + // Event::EvaluationRewarded { + // project_id, + // evaluator: evaluator.clone(), + // id: stored_evaluation.id, + // amount: total_reward, + // caller: evaluator, + // } + // .into(), + // ); + } + + #[benchmark] + fn settle_failed_evaluation() { // setup let mut inst = BenchInstantiator::::new(None); @@ -2210,20 +1483,20 @@ mod benchmarks { let issuer = account::>("issuer", 0, 0); let evaluations = default_evaluations::(); + let evaluator = evaluations[0].account.clone(); + whitelist_account!(evaluator); let project_metadata = default_project::(inst.get_new_nonce(), issuer.clone()); let target_funding_amount: BalanceOf = project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size); - let bids: Vec> = BenchInstantiator::generate_bids_from_total_usd( + let bids = BenchInstantiator::generate_bids_from_total_usd( Percent::from_percent(15) * target_funding_amount, 10u128.into(), default_weights(), default_bidders::(), default_bidder_multipliers(), ); - let bidder = bids[0].bidder.clone(); - whitelist_account!(bidder); let contributions = BenchInstantiator::generate_contributions_from_total_usd( Percent::from_percent(10) * target_funding_amount, project_metadata.minimum_price, @@ -2233,45 +1506,87 @@ mod benchmarks { ); let project_id = - inst.create_finished_project(project_metadata, issuer.clone(), evaluations, bids, contributions, vec![]); + inst.create_finished_project(project_metadata, issuer, evaluations, bids, contributions, vec![]); inst.advance_time(One::one()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Initialized(PhantomData)) + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); + + let evaluation_to_settle = + inst.execute(|| Evaluations::::iter_prefix_values((project_id, evaluator.clone())).next().unwrap()); + + #[extrinsic_call] + settle_failed_evaluation( + RawOrigin::Signed(evaluator.clone()), + project_id, + evaluator.clone(), + evaluation_to_settle.id, + ); + + // * validity checks * + // Storage + // Evaluation should be removed + assert!(Evaluations::::get((project_id, evaluator.clone(), evaluation_to_settle.id)).is_none()); + let slashed_amount = T::EvaluatorSlash::get() * evaluation_to_settle.original_plmc_bond; + + let reserved_plmc = inst.get_reserved_plmc_balances_for(vec![evaluator.clone()], HoldReason::Evaluation(project_id).into())[0].plmc_amount; + assert_eq!(reserved_plmc, 0.into()); + + let treasury_account = T::ProtocolGrowthTreasury::get(); + let free_treasury_plmc = inst.get_free_plmc_balances_for(vec![treasury_account])[0].plmc_amount; + let ed = BenchInstantiator::::get_ed(); + assert_eq!(free_treasury_plmc, slashed_amount + ed); + + // Events + // TODO assert Event + } + + #[benchmark] + fn settle_successful_bid() { + // setup + let mut inst = BenchInstantiator::::new(None); + + // real benchmark starts at block 0, and we can't call `events()` at block 0 + inst.advance_time(1u32.into()).unwrap(); + + let issuer = account::>("issuer", 0, 0); + let bids = default_bids::(); + let bidder = bids[0].bidder.clone(); + whitelist_account!(bidder); + + let project_id = inst.create_finished_project( + default_project::(inst.get_new_nonce(), issuer.clone()), + issuer, + default_evaluations::(), + bids, + default_community_contributions::(), + vec![], ); - let bid_to_payout = + run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); + + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); + + let bid_to_settle = inst.execute(|| Bids::::iter_prefix_values((project_id, bidder.clone())).next().unwrap()); - let asset = bid_to_payout.funding_asset.to_assethub_id(); - let free_assets_before = inst.get_free_foreign_asset_balances_for(asset, vec![bidder.clone()])[0].asset_amount; + #[extrinsic_call] - release_bid_funds_for(RawOrigin::Signed(issuer.clone()), project_id, bidder.clone(), bid_to_payout.id); + settle_successful_bid(RawOrigin::Signed(bidder.clone()), project_id, bidder.clone(), bid_to_settle.id); // * validity checks * // Storage - let stored_bid = Bids::::get((project_id, bidder.clone(), bid_to_payout.id)).unwrap(); - assert!(stored_bid.funds_released); + assert!(Bids::::get((project_id, bidder.clone(), bid_to_settle.id)).is_none()); + // Balances - let free_assets = inst.get_free_foreign_asset_balances_for(asset, vec![bidder.clone()])[0].asset_amount; - assert_eq!(free_assets, stored_bid.funding_asset_amount_locked + free_assets_before); + let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![bidder.clone()])[0]; + assert_eq!(bid_to_settle.final_ct_amount, ct_amount); // Events - frame_system::Pallet::::assert_last_event( - Event::BidFundingReleased { - project_id, - bidder, - id: stored_bid.id, - amount: stored_bid.funding_asset_amount_locked, - caller: issuer, - } - .into(), - ); + // TODO assert Event } #[benchmark] - fn release_contribution_funds_for() { + fn settle_successful_contribution() { // setup let mut inst = BenchInstantiator::::new(None); @@ -2279,77 +1594,51 @@ mod benchmarks { inst.advance_time(1u32.into()).unwrap(); let issuer = account::>("issuer", 0, 0); - let evaluations = default_evaluations::(); - - let project_metadata = default_project::(inst.get_new_nonce(), issuer.clone()); - let target_funding_amount: BalanceOf = - project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size); - - let bids: Vec> = BenchInstantiator::generate_bids_from_total_usd( - Percent::from_percent(15) * target_funding_amount, - project_metadata.minimum_price, - default_weights(), - default_bidders::(), - default_bidder_multipliers(), - ); - let contributions: Vec> = BenchInstantiator::generate_contributions_from_total_usd( - Percent::from_percent(10) * target_funding_amount, - project_metadata.minimum_price, - default_weights(), - default_community_contributors::(), - default_community_contributor_multipliers(), - ); + let contributions = default_community_contributions::(); let contributor = contributions[0].contributor.clone(); whitelist_account!(contributor); - let project_id = - inst.create_finished_project(project_metadata, issuer, evaluations, bids, contributions, vec![]); - - inst.advance_time(One::one()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Initialized(PhantomData)) + let project_id = inst.create_finished_project( + default_project::(inst.get_new_nonce(), issuer.clone()), + issuer, + default_evaluations::(), + default_bids::(), + contributions, + vec![], ); - let contribution_to_payout = + run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); + + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); + + let contribution_to_settle = inst.execute(|| Contributions::::iter_prefix_values((project_id, contributor.clone())).next().unwrap()); - let asset = contribution_to_payout.funding_asset.to_assethub_id(); - let free_assets_before = - inst.get_free_foreign_asset_balances_for(asset, vec![contributor.clone()])[0].asset_amount; #[extrinsic_call] - release_contribution_funds_for( + settle_successful_contribution( RawOrigin::Signed(contributor.clone()), project_id, contributor.clone(), - contribution_to_payout.id, + contribution_to_settle.id, ); // * validity checks * // Storage - let stored_contribution = - Contributions::::get((project_id, contributor.clone(), contribution_to_payout.id)).unwrap(); - assert!(stored_contribution.funds_released); + assert!(Contributions::::get((project_id, contributor.clone(), contribution_to_settle.id)).is_none()); // Balances - let free_assets = inst.get_free_foreign_asset_balances_for(asset, vec![contributor.clone()])[0].asset_amount; - assert_eq!(free_assets, stored_contribution.funding_asset_amount + free_assets_before); + let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![contributor.clone()])[0]; + assert_eq!(contribution_to_settle.ct_amount, ct_amount); // Events - frame_system::Pallet::::assert_last_event( - Event::ContributionFundingReleased { - project_id, - contributor: contributor.clone(), - id: stored_contribution.id, - amount: stored_contribution.funding_asset_amount, - caller: contributor, - } - .into(), - ); + // TODO assert Event } + + + #[benchmark] - fn bid_unbond_for() { + fn settle_failed_bid() { // setup let mut inst = BenchInstantiator::::new(None); @@ -2381,47 +1670,32 @@ mod benchmarks { ); let project_id = - inst.create_finished_project(project_metadata, issuer, evaluations, bids, contributions, vec![]); + inst.create_finished_project(project_metadata, issuer.clone(), evaluations, bids, contributions, vec![]); inst.advance_time(One::one()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Initialized(PhantomData)) - ); - - let stored_bid = inst.execute(|| Bids::::iter_prefix_values((project_id, bidder.clone())).next().unwrap()); - - inst.execute(|| { - PalletFunding::::release_bid_funds_for( - ::RuntimeOrigin::signed(bidder.clone().into()), - project_id, - bidder.clone(), - stored_bid.id, - ) - .expect("Funds are released") - }); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); + let bid_to_settle = + inst.execute(|| Bids::::iter_prefix_values((project_id, bidder.clone())).next().unwrap()); + let asset = bid_to_settle.funding_asset.to_assethub_id(); + let free_assets_before = inst.get_free_foreign_asset_balances_for(asset, vec![bidder.clone()])[0].asset_amount; #[extrinsic_call] - bid_unbond_for(RawOrigin::Signed(bidder.clone()), project_id, bidder.clone(), stored_bid.id); + settle_failed_bid(RawOrigin::Signed(issuer.clone()), project_id, bidder.clone(), bid_to_settle.id); // * validity checks * // Storage - assert!(!Bids::::contains_key((project_id, bidder.clone(), stored_bid.id))); + assert!(Bids::::get((project_id, bidder.clone(), bid_to_settle.id)).is_none()); + // Balances - let reserved_plmc = inst - .get_reserved_plmc_balances_for(vec![bidder.clone()], HoldReason::Participation(project_id).into())[0] - .plmc_amount; - assert_eq!(reserved_plmc, 0.into()); + let free_assets = inst.get_free_foreign_asset_balances_for(asset, vec![bidder.clone()])[0].asset_amount; + assert_eq!(free_assets, bid_to_settle.funding_asset_amount_locked + free_assets_before); // Events - frame_system::Pallet::::assert_last_event( - Event::BondReleased { project_id, amount: stored_bid.plmc_bond, bonder: bidder.clone(), releaser: bidder } - .into(), - ); + // TODO assert Event } #[benchmark] - fn contribution_unbond_for() { + fn settle_failed_contribution() { // setup let mut inst = BenchInstantiator::::new(None); @@ -2453,60 +1727,37 @@ mod benchmarks { whitelist_account!(contributor); let project_id = - inst.create_finished_project(project_metadata, issuer.clone(), evaluations, bids, contributions, vec![]); + inst.create_finished_project(project_metadata, issuer, evaluations, bids, contributions, vec![]); inst.advance_time(One::one()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Initialized(PhantomData)) - ); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - let stored_contribution = + let contribution_to_settle = inst.execute(|| Contributions::::iter_prefix_values((project_id, contributor.clone())).next().unwrap()); - inst.execute(|| { - PalletFunding::::release_contribution_funds_for( - ::RuntimeOrigin::signed(contributor.clone().into()), - project_id, - contributor.clone(), - stored_contribution.id, - ) - .expect("Funds are released") - }); - + let asset = contribution_to_settle.funding_asset.to_assethub_id(); + let free_assets_before = + inst.get_free_foreign_asset_balances_for(asset, vec![contributor.clone()])[0].asset_amount; #[extrinsic_call] - contribution_unbond_for( - RawOrigin::Signed(issuer.clone()), + settle_failed_contribution( + RawOrigin::Signed(contributor.clone()), project_id, contributor.clone(), - stored_contribution.id, + contribution_to_settle.id, ); // * validity checks * // Storage - assert!(!Contributions::::contains_key((project_id, contributor.clone(), stored_contribution.id))); + assert!(Contributions::::get((project_id, contributor.clone(), contribution_to_settle.id)).is_none()); + // Balances - let reserved_plmc = inst - .get_reserved_plmc_balances_for(vec![contributor.clone()], HoldReason::Participation(project_id).into())[0] - .plmc_amount; - assert_eq!(reserved_plmc, 0.into()); + let free_assets = inst.get_free_foreign_asset_balances_for(asset, vec![contributor.clone()])[0].asset_amount; + assert_eq!(free_assets, contribution_to_settle.funding_asset_amount + free_assets_before); // Events - frame_system::Pallet::::assert_last_event( - Event::BondReleased { - project_id, - amount: stored_contribution.plmc_bond, - bonder: contributor, - releaser: issuer, - } - .into(), - ); + // TODO assert Event } - // - // on_initialize - // - //do_evaluation_end #[benchmark] fn end_evaluation_success( @@ -2801,7 +2052,7 @@ mod benchmarks { Bids::::iter_prefix_values((project_id,)).filter(|b| matches!(b.status, BidStatus::Accepted)).count(); let rejected_bids_count = Bids::::iter_prefix_values((project_id,)).filter(|b| matches!(b.status, BidStatus::Rejected(_))).count(); - assert_eq!(rejected_bids_count, z as usize); + assert_eq!(rejected_bids_count, 0); assert_eq!(accepted_bids_count, y as usize); // Events @@ -3400,121 +2651,51 @@ mod benchmarks { } #[test] - fn bench_evaluation_unbond_for() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_evaluation_unbond_for()); - }); - } - - #[test] - fn bench_evaluation_reward_payout_for_with_ct_account_creation() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_evaluation_reward_payout_for_with_ct_account_creation()); - }); - } - - #[test] - fn bench_evaluation_reward_payout_for_no_ct_account_creation() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_evaluation_reward_payout_for_no_ct_account_creation()); - }); - } - - #[test] - fn bench_evaluation_slash_for() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_evaluation_slash_for()); - }); - } - - #[test] - fn bench_bid_ct_mint_for_with_ct_account_creation() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_bid_ct_mint_for_with_ct_account_creation()); - }); - } - - #[test] - fn bench_bid_ct_mint_for_no_ct_account_creation() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_bid_ct_mint_for_no_ct_account_creation()); - }); - } - - #[test] - fn bench_contribution_ct_mint_for_with_ct_account_creation() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_contribution_ct_mint_for_with_ct_account_creation()); - }); - } - - #[test] - fn bench_contribution_ct_mint_for_no_ct_account_creation() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_contribution_ct_mint_for_no_ct_account_creation()); - }); - } - - #[test] - fn bench_start_bid_vesting_schedule_for() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_start_bid_vesting_schedule_for()); - }); - } - - #[test] - fn bench_start_contribution_vesting_schedule_for() { - new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_start_contribution_vesting_schedule_for()); - }); - } - - #[test] - fn bench_payout_bid_funds_for() { + fn bench_decide_project_outcome() { new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_payout_bid_funds_for()); + assert_ok!(PalletFunding::::test_decide_project_outcome()); }); } #[test] - fn bench_payout_contribution_funds_for() { + fn bench_settle_successful_evaluation() { new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_payout_contribution_funds_for()); + assert_ok!(PalletFunding::::test_settle_successful_evaluation()); }); } #[test] - fn bench_decide_project_outcome() { + fn bench_settle_failed_evaluation() { new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_decide_project_outcome()); + assert_ok!(PalletFunding::::test_settle_failed_evaluation()); }); } #[test] - fn bench_release_bid_funds_for() { + fn bench_settle_successful_bid() { new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_release_bid_funds_for()); + assert_ok!(PalletFunding::::test_settle_successful_bid()); }); } #[test] - fn bench_release_contribution_funds_for() { + fn bench_settle_failed_bid() { new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_release_contribution_funds_for()); + assert_ok!(PalletFunding::::test_settle_failed_bid()); }); } #[test] - fn bench_bid_unbond_for() { + fn bench_settle_successful_contribution() { new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_bid_unbond_for()); + assert_ok!(PalletFunding::::test_settle_successful_contribution()); }); } #[test] - fn bench_contribution_unbond_for() { + fn bench_settle_failed_contribution() { new_test_ext().execute_with(|| { - assert_ok!(PalletFunding::::test_contribution_unbond_for()); + assert_ok!(PalletFunding::::test_settle_failed_contribution()); }); } diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index 61be66655..f94d6064a 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -1422,647 +1422,6 @@ impl Pallet { }) } - pub fn do_bid_ct_mint_for( - releaser: &AccountIdOf, - project_id: ProjectId, - bidder: &AccountIdOf, - bid_id: u32, - ) -> DispatchResultWithPostInfo { - // * Get variables * - let mut bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::BidNotFound)?; - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectNotFound)?; - let ct_amount = bid.final_ct_amount; - - // weight return variables - let mut ct_account_created = false; - - // * Validity checks * - ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); - ensure!(!bid.ct_minted, Error::::NotAllowed); - ensure!(matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), Error::::NotAllowed); - ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); - - // * Calculate variables * - bid.ct_minted = true; - - // * Update storage * - if !T::ContributionTokenCurrency::contains(&project_id, &bid.bidder) { - ct_account_created = true; - T::ContributionTokenCurrency::touch(project_id, bid.bidder.clone(), bid.bidder.clone())?; - } - T::ContributionTokenCurrency::mint_into(project_id, &bid.bidder, ct_amount)?; - Bids::::insert((project_id, bidder, bid_id), &bid); - - // * Emit events * - Self::deposit_event(Event::ContributionTokenMinted { - releaser: releaser.clone(), - project_id: bid.project_id, - claimer: bidder.clone(), - amount: ct_amount, - }); - - Ok(PostDispatchInfo { - actual_weight: Some(if ct_account_created { - WeightInfoOf::::bid_ct_mint_for_with_ct_account_creation() - } else { - WeightInfoOf::::bid_ct_mint_for_no_ct_account_creation() - }), - pays_fee: Pays::Yes, - }) - } - - pub fn do_contribution_ct_mint_for( - releaser: &AccountIdOf, - project_id: ProjectId, - contributor: &AccountIdOf, - contribution_id: u32, - ) -> DispatchResultWithPostInfo { - // * Get variables * - let mut contribution = - Contributions::::get((project_id, contributor, contribution_id)).ok_or(Error::::BidNotFound)?; - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectNotFound)?; - let ct_amount = contribution.ct_amount; - - // weight return variables - let mut ct_account_created = false; - - // * Validity checks * - ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); - ensure!(!contribution.ct_minted, Error::::NotAllowed); - ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); - - // * Calculate variables * - contribution.ct_minted = true; - - // * Update storage * - if !T::ContributionTokenCurrency::contains(&project_id, &contribution.contributor) { - ct_account_created = true; - T::ContributionTokenCurrency::touch( - project_id, - contribution.contributor.clone(), - contribution.contributor.clone(), - )?; - } - T::ContributionTokenCurrency::mint_into(project_id, &contribution.contributor, ct_amount)?; - Contributions::::insert((project_id, contributor, contribution_id), contribution); - - // * Emit events * - Self::deposit_event(Event::ContributionTokenMinted { - releaser: releaser.clone(), - project_id, - claimer: contributor.clone(), - amount: ct_amount, - }); - - Ok(PostDispatchInfo { - actual_weight: Some(if ct_account_created { - WeightInfoOf::::contribution_ct_mint_for_with_ct_account_creation() - } else { - WeightInfoOf::::contribution_ct_mint_for_no_ct_account_creation() - }), - pays_fee: Pays::Yes, - }) - } - - pub fn do_evaluation_unbond_for( - releaser: &AccountIdOf, - project_id: ProjectId, - evaluator: &AccountIdOf, - evaluation_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let mut released_evaluation = - Evaluations::::get((project_id, evaluator, evaluation_id)).ok_or(Error::::EvaluationNotFound)?; - let release_amount = released_evaluation.current_plmc_bond; - - // * Validity checks * - ensure!( - (project_details.evaluation_round_info.evaluators_outcome == EvaluatorsOutcomeOf::::Unchanged || - released_evaluation.rewarded_or_slashed.is_some()) && - matches!( - project_details.status, - ProjectStatus::EvaluationFailed | ProjectStatus::FundingFailed | ProjectStatus::FundingSuccessful - ), - Error::::NotAllowed - ); - - // * Update Storage * - T::NativeCurrency::release( - &HoldReason::Evaluation(project_id).into(), - evaluator, - released_evaluation.current_plmc_bond, - Precision::Exact, - )?; - - released_evaluation.current_plmc_bond = Zero::zero(); - Evaluations::::insert((project_id, evaluator, evaluation_id), released_evaluation); - - // FIXME: same question as removing bid - // Evaluations::::remove((project_id, evaluator, evaluation_id)); - - // * Emit events * - Self::deposit_event(Event::BondReleased { - project_id, - amount: release_amount, - bonder: evaluator.clone(), - releaser: releaser.clone(), - }); - - Ok(()) - } - - pub fn do_evaluation_reward_payout_for( - caller: &AccountIdOf, - project_id: ProjectId, - evaluator: &AccountIdOf, - evaluation_id: u32, - ) -> DispatchResultWithPostInfo { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let reward_info = - if let EvaluatorsOutcome::Rewarded(info) = project_details.evaluation_round_info.evaluators_outcome { - info - } else { - return Err(Error::::NotAllowed.into()); - }; - let mut evaluation = - Evaluations::::get((project_id, evaluator, evaluation_id)).ok_or(Error::::EvaluationNotFound)?; - - // weight return variables - let mut ct_account_created = false; - - // * Validity checks * - ensure!( - evaluation.rewarded_or_slashed.is_none() && - matches!(project_details.status, ProjectStatus::FundingSuccessful), - Error::::NotAllowed - ); - - // * Calculate variables * - let early_reward_weight = - Perquintill::from_rational(evaluation.early_usd_amount, reward_info.early_evaluator_total_bonded_usd); - let normal_reward_weight = Perquintill::from_rational( - evaluation.late_usd_amount.saturating_add(evaluation.early_usd_amount), - reward_info.normal_evaluator_total_bonded_usd, - ); - let early_evaluators_rewards = early_reward_weight * reward_info.early_evaluator_reward_pot; - let normal_evaluators_rewards = normal_reward_weight * reward_info.normal_evaluator_reward_pot; - let total_reward_amount = early_evaluators_rewards.saturating_add(normal_evaluators_rewards); - - // * Update storage * - if !T::ContributionTokenCurrency::contains(&project_id, &evaluation.evaluator) { - ct_account_created = true; - T::ContributionTokenCurrency::touch( - project_id, - evaluation.evaluator.clone(), - evaluation.evaluator.clone(), - )?; - } - T::ContributionTokenCurrency::mint_into(project_id, &evaluation.evaluator, total_reward_amount)?; - evaluation.rewarded_or_slashed = Some(RewardOrSlash::Reward(total_reward_amount)); - Evaluations::::insert((project_id, evaluator, evaluation_id), evaluation); - - // * Emit events * - Self::deposit_event(Event::EvaluationRewarded { - project_id, - evaluator: evaluator.clone(), - id: evaluation_id, - amount: total_reward_amount, - caller: caller.clone(), - }); - - Ok(if ct_account_created { - PostDispatchInfo { - actual_weight: Some(WeightInfoOf::::evaluation_reward_payout_for_with_ct_account_creation()), - pays_fee: Pays::Yes, - } - } else { - PostDispatchInfo { - actual_weight: Some(WeightInfoOf::::evaluation_reward_payout_for_no_ct_account_creation()), - pays_fee: Pays::Yes, - } - }) - } - - pub fn do_evaluation_slash_for( - caller: &AccountIdOf, - project_id: ProjectId, - evaluator: &AccountIdOf, - evaluation_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let slash_percentage = T::EvaluatorSlash::get(); - let treasury_account = T::ProtocolGrowthTreasury::get(); - - let mut evaluation = - Evaluations::::get((project_id, evaluator, evaluation_id)).ok_or(Error::::EvaluationNotFound)?; - - // * Validity checks * - ensure!( - evaluation.rewarded_or_slashed.is_none() && - matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed), - Error::::NotAllowed - ); - - // * Calculate variables * - // We need to make sure that the current PLMC bond is always >= than the slash amount. - let slashed_amount = slash_percentage * evaluation.original_plmc_bond; - - // * Update storage * - evaluation.rewarded_or_slashed = Some(RewardOrSlash::Slash(slashed_amount)); - - T::NativeCurrency::transfer_on_hold( - &HoldReason::Evaluation(project_id).into(), - evaluator, - &treasury_account, - slashed_amount, - Precision::Exact, - Restriction::Free, - Fortitude::Force, - )?; - - evaluation.current_plmc_bond.saturating_reduce(slashed_amount); - Evaluations::::insert((project_id, evaluator, evaluation.id), evaluation); - - // * Emit events * - Self::deposit_event(Event::EvaluationSlashed { - project_id, - evaluator: evaluator.clone(), - id: evaluation_id, - amount: slashed_amount, - caller: caller.clone(), - }); - - Ok(()) - } - - pub fn do_start_bid_vesting_schedule_for( - caller: &AccountIdOf, - project_id: ProjectId, - bidder: &AccountIdOf, - bid_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let mut bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::BidNotFound)?; - let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; - - // * Validity checks * - ensure!( - bid.plmc_vesting_info.is_none() && - project_details.status == ProjectStatus::FundingSuccessful && - matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), - Error::::NotAllowed - ); - - // * Calculate variables * - let vest_info = - Self::calculate_vesting_info(bidder, bid.multiplier, bid.plmc_bond).map_err(|_| Error::::BadMath)?; - bid.plmc_vesting_info = Some(vest_info); - - // * Update storage * - T::Vesting::add_release_schedule( - bidder, - vest_info.total_amount, - vest_info.amount_per_block, - funding_end_block, - HoldReason::Participation(project_id).into(), - )?; - Bids::::insert((project_id, bidder, bid_id), bid); - - // * Emit events * - Self::deposit_event(Event::BidPlmcVestingScheduled { - project_id, - bidder: bidder.clone(), - id: bid_id, - amount: vest_info.total_amount, - caller: caller.clone(), - }); - - Ok(()) - } - - pub fn do_start_contribution_vesting_schedule_for( - caller: &AccountIdOf, - project_id: ProjectId, - contributor: &AccountIdOf, - contribution_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let mut contribution = - Contributions::::get((project_id, contributor, contribution_id)).ok_or(Error::::BidNotFound)?; - let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; - - // * Validity checks * - ensure!( - contribution.plmc_vesting_info.is_none() && project_details.status == ProjectStatus::FundingSuccessful, - Error::::NotAllowed - ); - - // * Calculate variables * - let vest_info = Self::calculate_vesting_info(contributor, contribution.multiplier, contribution.plmc_bond) - .map_err(|_| Error::::BadMath)?; - contribution.plmc_vesting_info = Some(vest_info); - - // * Update storage * - T::Vesting::add_release_schedule( - contributor, - vest_info.total_amount, - vest_info.amount_per_block, - funding_end_block, - HoldReason::Participation(project_id).into(), - )?; - Contributions::::insert((project_id, contributor, contribution_id), contribution); - - // * Emit events * - Self::deposit_event(Event::ContributionPlmcVestingScheduled { - project_id, - contributor: contributor.clone(), - id: contribution_id, - amount: vest_info.total_amount, - caller: caller.clone(), - }); - - Ok(()) - } - - pub fn do_vest_plmc_for( - caller: AccountIdOf, - project_id: ProjectId, - participant: AccountIdOf, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - - // * Validity checks * - ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::::NotAllowed); - - // * Update storage * - let vested_amount = T::Vesting::vest(participant.clone(), HoldReason::Participation(project_id).into())?; - - // * Emit events * - Self::deposit_event(Event::ParticipantPlmcVested { project_id, participant, amount: vested_amount, caller }); - - Ok(()) - } - - pub fn do_release_bid_funds_for( - caller: &AccountIdOf, - project_id: ProjectId, - bidder: &AccountIdOf, - bid_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let mut bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::BidNotFound)?; - - // * Validity checks * - ensure!( - project_details.status == ProjectStatus::FundingFailed && - matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), - Error::::NotAllowed - ); - - // * Calculate variables * - let project_pot = Self::fund_account_id(project_id); - let payout_amount = bid.funding_asset_amount_locked; - let payout_asset = bid.funding_asset; - - // * Update storage * - T::FundingCurrency::transfer( - payout_asset.to_assethub_id(), - &project_pot, - bidder, - payout_amount, - Preservation::Expendable, - )?; - bid.funds_released = true; - Bids::::insert((project_id, bidder, bid_id), bid); - - // * Emit events * - Self::deposit_event(Event::BidFundingReleased { - project_id, - bidder: bidder.clone(), - id: bid_id, - amount: payout_amount, - caller: caller.clone(), - }); - - Ok(()) - } - - // Unbond the PLMC of a bid instantly, following a failed funding outcome. - // Unbonding of PLMC in a successful funding outcome is handled by the vesting schedule. - pub fn do_bid_unbond_for( - caller: &AccountIdOf, - project_id: ProjectId, - bidder: &AccountIdOf, - bid_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::EvaluationNotFound)?; - - // * Validity checks * - ensure!( - project_details.status == ProjectStatus::FundingFailed && - matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) && - bid.funds_released, - Error::::NotAllowed - ); - - // * Update Storage * - T::NativeCurrency::release( - &HoldReason::Participation(project_id).into(), - bidder, - bid.plmc_bond, - Precision::Exact, - )?; - - Bids::::remove((project_id, bidder, bid_id)); - - // * Emit events * - Self::deposit_event(Event::BondReleased { - project_id, - amount: bid.plmc_bond, - bonder: bidder.clone(), - releaser: caller.clone(), - }); - - Ok(()) - } - - pub fn do_release_contribution_funds_for( - caller: &AccountIdOf, - project_id: ProjectId, - contributor: &AccountIdOf, - contribution_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let mut contribution = Contributions::::get((project_id, contributor, contribution_id)) - .ok_or(Error::::ContributionNotFound)?; - - // * Validity checks * - ensure!(project_details.status == ProjectStatus::FundingFailed, Error::::NotAllowed); - - // * Calculate variables * - let project_pot = Self::fund_account_id(project_id); - let payout_amount = contribution.funding_asset_amount; - let payout_asset = contribution.funding_asset; - - // * Update storage * - T::FundingCurrency::transfer( - payout_asset.to_assethub_id(), - &project_pot, - contributor, - payout_amount, - Preservation::Expendable, - )?; - contribution.funds_released = true; - Contributions::::insert((project_id, contributor, contribution_id), contribution); - - // * Emit events * - Self::deposit_event(Event::ContributionFundingReleased { - project_id, - contributor: contributor.clone(), - id: contribution_id, - amount: payout_amount, - caller: caller.clone(), - }); - - Ok(()) - } - - // Unbond the PLMC of a contribution instantly, following a failed funding outcome. - // Unbonding of PLMC in a successful funding outcome is handled by the vesting schedule. - pub fn do_contribution_unbond_for( - caller: &AccountIdOf, - project_id: ProjectId, - contributor: &AccountIdOf, - contribution_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let bid = Contributions::::get((project_id, contributor, contribution_id)) - .ok_or(Error::::EvaluationNotFound)?; - - // * Validity checks * - ensure!(project_details.status == ProjectStatus::FundingFailed, Error::::NotAllowed); - - // * Update Storage * - T::NativeCurrency::release( - &HoldReason::Participation(project_id).into(), - contributor, - bid.plmc_bond, - Precision::Exact, - )?; - - Contributions::::remove((project_id, contributor, contribution_id)); - - // * Emit events * - Self::deposit_event(Event::BondReleased { - project_id, - amount: bid.plmc_bond, - bonder: contributor.clone(), - releaser: caller.clone(), - }); - - Ok(()) - } - - pub fn do_payout_bid_funds_for( - caller: &AccountIdOf, - project_id: ProjectId, - bidder: &AccountIdOf, - bid_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let mut bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::BidNotFound)?; - - // * Validity checks * - ensure!( - project_details.status == ProjectStatus::FundingSuccessful && - matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), - Error::::NotAllowed - ); - - // * Calculate variables * - let issuer = project_details.issuer_account; - let project_pot = Self::fund_account_id(project_id); - let payout_amount = bid.funding_asset_amount_locked; - let payout_asset = bid.funding_asset; - - // * Update storage * - T::FundingCurrency::transfer( - payout_asset.to_assethub_id(), - &project_pot, - &issuer, - payout_amount, - Preservation::Expendable, - )?; - bid.funds_released = true; - Bids::::insert((project_id, bidder, bid_id), &bid); - - // * Emit events * - Self::deposit_event(Event::BidFundingPaidOut { - project_id, - bidder: bidder.clone(), - id: bid_id, - amount: payout_amount, - caller: caller.clone(), - }); - - Ok(()) - } - - pub fn do_payout_contribution_funds_for( - caller: &AccountIdOf, - project_id: ProjectId, - contributor: &AccountIdOf, - contribution_id: u32, - ) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let mut contribution = - Contributions::::get((project_id, contributor, contribution_id)).ok_or(Error::::BidNotFound)?; - - // * Validity checks * - ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); - - // * Calculate variables * - let issuer = project_details.issuer_account; - let project_pot = Self::fund_account_id(project_id); - let payout_amount = contribution.funding_asset_amount; - let payout_asset = contribution.funding_asset; - - // * Update storage * - T::FundingCurrency::transfer( - payout_asset.to_assethub_id(), - &project_pot, - &issuer, - payout_amount, - Preservation::Expendable, - )?; - contribution.funds_released = true; - Contributions::::insert((project_id, contributor, contribution_id), contribution); - - // * Emit events * - Self::deposit_event(Event::ContributionFundingPaidOut { - project_id, - contributor: contributor.clone(), - id: contribution_id, - amount: payout_amount, - caller: caller.clone(), - }); - - Ok(()) - } - pub fn do_set_para_id_for_project( caller: &AccountIdOf, project_id: ProjectId, diff --git a/pallets/funding/src/impls.rs b/pallets/funding/src/impls.rs index 76c3929ac..27d43748d 100644 --- a/pallets/funding/src/impls.rs +++ b/pallets/funding/src/impls.rs @@ -14,709 +14,709 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use frame_support::{ - dispatch::{DispatchErrorWithPostInfo, GetDispatchInfo}, - traits::Get, - weights::Weight, -}; -use sp_arithmetic::traits::Zero; -use sp_runtime::{traits::AccountIdConversion, DispatchError}; -use sp_std::marker::PhantomData; - -use crate::{traits::DoRemainingOperation, *}; - -impl DoRemainingOperation for Cleaner { - fn has_remaining_operations(&self) -> bool { - match self { - Cleaner::NotReady => false, - Cleaner::Success(state) => - as DoRemainingOperation>::has_remaining_operations(state), - Cleaner::Failure(state) => - as DoRemainingOperation>::has_remaining_operations(state), - } - } - - fn do_one_operation(&mut self, project_id: ProjectId) -> Result { - match self { - Cleaner::NotReady => Err(DispatchError::Other("Cleaner not ready")), - Cleaner::Success(state) => - as DoRemainingOperation>::do_one_operation(state, project_id), - Cleaner::Failure(state) => - as DoRemainingOperation>::do_one_operation(state, project_id), - } - } -} - -impl DoRemainingOperation for CleanerState { - fn has_remaining_operations(&self) -> bool { - !matches!(self, CleanerState::Finished(_)) - } - - fn do_one_operation(&mut self, project_id: ProjectId) -> Result { - let evaluators_outcome = ProjectsDetails::::get(project_id) - .ok_or(Error::::ImpossibleState)? - .evaluation_round_info - .evaluators_outcome; - let base_weight = Weight::from_parts(10_000_000, 0); - - match self { - CleanerState::Initialized(PhantomData) => { - *self = Self::EvaluationRewardOrSlash( - remaining_evaluators_to_reward_or_slash::(project_id, evaluators_outcome), - PhantomData, - ); - Ok(base_weight) - }, - CleanerState::EvaluationRewardOrSlash(remaining, PhantomData) => - if *remaining == 0 { - *self = Self::EvaluationUnbonding(remaining_evaluations::(project_id), PhantomData); - Ok(base_weight) - } else { - let (consumed_weight, remaining_evaluations) = - reward_or_slash_one_evaluation::(project_id).map_err(|error_info| error_info.error)?; - *self = CleanerState::EvaluationRewardOrSlash(remaining_evaluations, PhantomData); - Ok(consumed_weight) - }, - CleanerState::EvaluationUnbonding(remaining, PhantomData) => - if *remaining == 0 { - *self = CleanerState::StartBidderVestingSchedule( - remaining_successful_bids::(project_id), - PhantomData, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_evaluations) = unbond_one_evaluation::(project_id); - *self = CleanerState::EvaluationUnbonding(remaining_evaluations, PhantomData); - Ok(consumed_weight) - }, - CleanerState::StartBidderVestingSchedule(remaining, PhantomData) => - if *remaining == 0 { - *self = CleanerState::StartContributorVestingSchedule( - remaining_contributions::(project_id), - PhantomData, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_evaluations) = start_one_bid_vesting_schedule::(project_id); - *self = CleanerState::StartBidderVestingSchedule(remaining_evaluations, PhantomData); - Ok(consumed_weight) - }, - CleanerState::StartContributorVestingSchedule(remaining, PhantomData) => - if *remaining == 0 { - *self = CleanerState::BidCTMint(remaining_bids_without_ct_minted::(project_id), PhantomData); - Ok(base_weight) - } else { - let (consumed_weight, remaining_evaluations) = - start_one_contribution_vesting_schedule::(project_id); - *self = CleanerState::StartContributorVestingSchedule(remaining_evaluations, PhantomData); - Ok(consumed_weight) - }, - CleanerState::BidCTMint(remaining, PhantomData) => - if *remaining == 0 { - *self = CleanerState::ContributionCTMint( - remaining_contributions_without_ct_minted::(project_id), - PhantomData, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_bids) = mint_ct_for_one_bid::(project_id); - *self = CleanerState::BidCTMint(remaining_bids, PhantomData); - Ok(consumed_weight) - }, - CleanerState::ContributionCTMint(remaining, PhantomData) => - if *remaining == 0 { - *self = CleanerState::BidFundingPayout( - remaining_bids_without_issuer_payout::(project_id), - PhantomData, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_contributions) = mint_ct_for_one_contribution::(project_id); - *self = CleanerState::ContributionCTMint(remaining_contributions, PhantomData); - Ok(consumed_weight) - }, - CleanerState::BidFundingPayout(remaining, PhantomData) => - if *remaining == 0 { - *self = CleanerState::ContributionFundingPayout( - remaining_contributions_without_issuer_payout::(project_id), - PhantomData, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_contributions) = issuer_funding_payout_one_bid::(project_id); - *self = CleanerState::BidFundingPayout(remaining_contributions, PhantomData); - Ok(consumed_weight) - }, - CleanerState::ContributionFundingPayout(remaining, PhantomData) => - if *remaining == 0 { - *self = CleanerState::Finished(PhantomData); - Ok(base_weight) - } else { - let (consumed_weight, remaining_contributions) = - issuer_funding_payout_one_contribution::(project_id); - *self = CleanerState::ContributionFundingPayout(remaining_contributions, PhantomData); - Ok(consumed_weight) - }, - CleanerState::Finished(PhantomData) => Err(Error::::FinalizerFinished.into()), - - _ => Err(Error::::ImpossibleState.into()), - } - } -} -impl DoRemainingOperation for CleanerState { - fn has_remaining_operations(&self) -> bool { - !matches!(self, CleanerState::Finished(PhantomData::)) - } - - fn do_one_operation(&mut self, project_id: ProjectId) -> Result { - let evaluators_outcome = ProjectsDetails::::get(project_id) - .ok_or(Error::::ImpossibleState)? - .evaluation_round_info - .evaluators_outcome; - let base_weight = Weight::from_parts(10_000_000, 0); - - match self { - CleanerState::Initialized(PhantomData::) => { - *self = CleanerState::EvaluationRewardOrSlash( - remaining_evaluators_to_reward_or_slash::(project_id, evaluators_outcome), - PhantomData::, - ); - Ok(base_weight) - }, - - CleanerState::EvaluationRewardOrSlash(remaining, PhantomData::) => - if *remaining == 0 { - *self = CleanerState::EvaluationUnbonding( - remaining_evaluations::(project_id), - PhantomData::, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_evaluators) = - reward_or_slash_one_evaluation::(project_id).map_err(|error_info| error_info.error)?; - *self = CleanerState::EvaluationRewardOrSlash(remaining_evaluators, PhantomData); - Ok(consumed_weight) - }, - - CleanerState::EvaluationUnbonding(remaining, PhantomData::) => - if *remaining == 0 { - *self = CleanerState::BidFundingRelease( - remaining_bids_to_release_funds::(project_id), - PhantomData::, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_evaluators) = unbond_one_evaluation::(project_id); - *self = CleanerState::EvaluationUnbonding(remaining_evaluators, PhantomData); - Ok(consumed_weight) - }, - - CleanerState::BidFundingRelease(remaining, PhantomData::) => - if *remaining == 0 { - *self = CleanerState::BidUnbonding(remaining_bids::(project_id), PhantomData::); - Ok(base_weight) - } else { - let (consumed_weight, remaining_bids) = release_funds_one_bid::(project_id); - *self = CleanerState::BidFundingRelease(remaining_bids, PhantomData); - Ok(consumed_weight) - }, - - CleanerState::BidUnbonding(remaining, PhantomData::) => - if *remaining == 0 { - *self = CleanerState::ContributionFundingRelease( - remaining_contributions_to_release_funds::(project_id), - PhantomData::, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_bids) = unbond_one_bid::(project_id); - *self = CleanerState::BidUnbonding(remaining_bids, PhantomData::); - Ok(consumed_weight) - }, - - CleanerState::ContributionFundingRelease(remaining, PhantomData::) => - if *remaining == 0 { - *self = CleanerState::ContributionUnbonding( - remaining_contributions::(project_id), - PhantomData::, - ); - Ok(base_weight) - } else { - let (consumed_weight, remaining_contributions) = release_funds_one_contribution::(project_id); - *self = CleanerState::ContributionFundingRelease(remaining_contributions, PhantomData::); - Ok(consumed_weight) - }, - - CleanerState::ContributionUnbonding(remaining, PhantomData::) => - if *remaining == 0 { - *self = CleanerState::Finished(PhantomData::); - Ok(base_weight) - } else { - let (consumed_weight, remaining_contributions) = unbond_one_contribution::(project_id); - *self = CleanerState::ContributionUnbonding(remaining_contributions, PhantomData::); - Ok(consumed_weight) - }, - - CleanerState::Finished(PhantomData::) => Err(Error::::FinalizerFinished.into()), - - _ => Err(Error::::ImpossibleState.into()), - } - } -} - -fn remaining_evaluators_to_reward_or_slash(project_id: ProjectId, outcome: EvaluatorsOutcomeOf) -> u64 { - if outcome == EvaluatorsOutcomeOf::::Unchanged { - 0u64 - } else { - Evaluations::::iter_prefix_values((project_id,)) - .filter(|evaluation| evaluation.rewarded_or_slashed.is_none()) - .count() as u64 - } -} - -fn remaining_evaluations(project_id: ProjectId) -> u64 { - Evaluations::::iter_prefix_values((project_id,)).count() as u64 -} - -fn remaining_bids_to_release_funds(project_id: ProjectId) -> u64 { - Bids::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 -} - -fn remaining_bids(project_id: ProjectId) -> u64 { - Bids::::iter_prefix_values((project_id,)).count() as u64 -} - -fn remaining_successful_bids(project_id: ProjectId) -> u64 { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) - .count() as u64 -} - -fn remaining_contributions_to_release_funds(project_id: ProjectId) -> u64 { - Contributions::::iter_prefix_values((project_id,)).filter(|contribution| !contribution.funds_released).count() - as u64 -} - -fn remaining_contributions(project_id: ProjectId) -> u64 { - Contributions::::iter_prefix_values((project_id,)).count() as u64 -} - -fn remaining_bids_without_ct_minted(project_id: ProjectId) -> u64 { - let project_bids = Bids::::iter_prefix_values((project_id,)); - project_bids.filter(|bid| !bid.ct_minted).count() as u64 -} - -fn remaining_contributions_without_ct_minted(project_id: ProjectId) -> u64 { - let project_contributions = Contributions::::iter_prefix_values((project_id,)); - project_contributions.filter(|contribution| !contribution.ct_minted).count() as u64 -} - -fn remaining_bids_without_issuer_payout(project_id: ProjectId) -> u64 { - Bids::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 -} - -fn remaining_contributions_without_issuer_payout(project_id: ProjectId) -> u64 { - Contributions::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 -} - -fn reward_or_slash_one_evaluation( - project_id: ProjectId, -) -> Result<(Weight, u64), DispatchErrorWithPostInfo> { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectNotFound)?; - let project_evaluations = Evaluations::::iter_prefix_values((project_id,)); - let mut remaining_evaluations = project_evaluations.filter(|evaluation| evaluation.rewarded_or_slashed.is_none()); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(evaluation) = remaining_evaluations.next() { - // TODO: This base weight and the one in all other functions below should be calculated with a benchmark - let remaining = remaining_evaluations.count() as u64; - match project_details.evaluation_round_info.evaluators_outcome { - EvaluatorsOutcome::Rewarded(_) => { - let mut weight_consumed = crate::Call::::evaluation_reward_payout_for { - project_id: evaluation.project_id, - evaluator: evaluation.evaluator.clone(), - bond_id: evaluation.id, - } - .get_dispatch_info() - .weight; - - match Pallet::::do_evaluation_reward_payout_for( - &T::PalletId::get().into_account_truncating(), - evaluation.project_id, - &evaluation.evaluator, - evaluation.id, - ) { - Ok(result) => { - if let Some(weight) = result.actual_weight { - weight_consumed = weight - }; - }, - Err(e) => { - if let Some(weight) = e.post_info.actual_weight { - weight_consumed = weight - }; - Pallet::::deposit_event(Event::EvaluationRewardFailed { - project_id: evaluation.project_id, - evaluator: evaluation.evaluator.clone(), - id: evaluation.id, - error: e.error, - }) - }, - }; - - Ok((base_weight.saturating_add(weight_consumed), remaining)) - }, - EvaluatorsOutcome::Slashed => { - match Pallet::::do_evaluation_slash_for( - &T::PalletId::get().into_account_truncating(), - evaluation.project_id, - &evaluation.evaluator, - evaluation.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::EvaluationSlashFailed { - project_id: evaluation.project_id, - evaluator: evaluation.evaluator.clone(), - id: evaluation.id, - error: e, - }), - }; - - Ok((base_weight.saturating_add(WeightInfoOf::::evaluation_slash_for()), remaining)) - }, - _ => { - #[cfg(debug_assertions)] - unreachable!("EvaluatorsOutcome should be either Slashed or Rewarded if this function is called"); - #[cfg(not(debug_assertions))] - Err(Error::::ImpossibleState.into()) - }, - } - } else { - Ok((base_weight, 0u64)) - } -} - -fn unbond_one_evaluation(project_id: ProjectId) -> (Weight, u64) { - let project_evaluations = Evaluations::::iter_prefix_values((project_id,)); - let mut remaining_evaluations = - project_evaluations.filter(|evaluation| evaluation.current_plmc_bond > Zero::zero()); - let base_weight = Weight::from_parts(10_000_000, 0); - if let Some(evaluation) = remaining_evaluations.next() { - match Pallet::::do_evaluation_unbond_for( - &T::PalletId::get().into_account_truncating(), - evaluation.project_id, - &evaluation.evaluator, - evaluation.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::EvaluationUnbondFailed { - project_id: evaluation.project_id, - evaluator: evaluation.evaluator.clone(), - id: evaluation.id, - error: e, - }), - }; - (base_weight.saturating_add(WeightInfoOf::::evaluation_unbond_for()), remaining_evaluations.count() as u64) - } else { - (base_weight, 0u64) - } -} - -fn release_funds_one_bid(project_id: ProjectId) -> (Weight, u64) { - let project_bids = Bids::::iter_prefix_values((project_id,)); - let mut remaining_bids = project_bids.filter(|bid| !bid.funds_released); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(bid) = remaining_bids.next() { - match Pallet::::do_release_bid_funds_for( - &T::PalletId::get().into_account_truncating(), - bid.project_id, - &bid.bidder, - bid.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::ReleaseBidFundsFailed { - project_id: bid.project_id, - bidder: bid.bidder.clone(), - id: bid.id, - error: e, - }), - }; - - (base_weight.saturating_add(WeightInfoOf::::release_bid_funds_for()), remaining_bids.count() as u64) - } else { - (base_weight, 0u64) - } -} - -fn unbond_one_bid(project_id: ProjectId) -> (Weight, u64) { - let project_bids = Bids::::iter_prefix_values((project_id,)); - let mut remaining_bids = project_bids.filter(|bid| bid.funds_released); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(bid) = remaining_bids.next() { - match Pallet::::do_bid_unbond_for( - &T::PalletId::get().into_account_truncating(), - bid.project_id, - &bid.bidder, - bid.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::BidUnbondFailed { - project_id: bid.project_id, - bidder: bid.bidder.clone(), - id: bid.id, - error: e, - }), - }; - (base_weight.saturating_add(WeightInfoOf::::bid_unbond_for()), remaining_bids.count() as u64) - } else { - (base_weight, 0u64) - } -} - -fn release_funds_one_contribution(project_id: ProjectId) -> (Weight, u64) { - let project_contributions = Contributions::::iter_prefix_values((project_id,)); - let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.funds_released); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(contribution) = remaining_contributions.next() { - match Pallet::::do_release_contribution_funds_for( - &T::PalletId::get().into_account_truncating(), - contribution.project_id, - &contribution.contributor, - contribution.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::ReleaseContributionFundsFailed { - project_id: contribution.project_id, - contributor: contribution.contributor.clone(), - id: contribution.id, - error: e, - }), - }; - - ( - base_weight.saturating_add(WeightInfoOf::::release_contribution_funds_for()), - remaining_contributions.count() as u64, - ) - } else { - (base_weight, 0u64) - } -} - -fn unbond_one_contribution(project_id: ProjectId) -> (Weight, u64) { - let project_contributions = Contributions::::iter_prefix_values((project_id,)); - - let mut remaining_contributions = - project_contributions.into_iter().filter(|contribution| contribution.funds_released); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(contribution) = remaining_contributions.next() { - match Pallet::::do_contribution_unbond_for( - &T::PalletId::get().into_account_truncating(), - contribution.project_id, - &contribution.contributor, - contribution.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::ContributionUnbondFailed { - project_id: contribution.project_id, - contributor: contribution.contributor.clone(), - id: contribution.id, - error: e, - }), - }; - ( - base_weight.saturating_add(WeightInfoOf::::contribution_unbond_for()), - remaining_contributions.count() as u64, - ) - } else { - (base_weight, 0u64) - } -} - -fn start_one_bid_vesting_schedule(project_id: ProjectId) -> (Weight, u64) { - let project_bids = Bids::::iter_prefix_values((project_id,)); - let mut unscheduled_bids = project_bids.filter(|bid| { - bid.plmc_vesting_info.is_none() && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) - }); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(bid) = unscheduled_bids.next() { - match Pallet::::do_start_bid_vesting_schedule_for( - &T::PalletId::get().into_account_truncating(), - project_id, - &bid.bidder, - bid.id, - ) { - Ok(_) => {}, - Err(e) => { - // TODO: Handle `MAX_VESTING_SCHEDULES` error - - Pallet::::deposit_event(Event::StartBidderVestingScheduleFailed { - project_id: bid.project_id, - bidder: bid.bidder.clone(), - id: bid.id, - error: e, - }); - }, - } - ( - base_weight.saturating_add(WeightInfoOf::::start_bid_vesting_schedule_for()), - unscheduled_bids.count() as u64, - ) - } else { - (base_weight, 0u64) - } -} - -fn start_one_contribution_vesting_schedule(project_id: ProjectId) -> (Weight, u64) { - let project_bids = Contributions::::iter_prefix_values((project_id,)); - let mut unscheduled_contributions = project_bids.filter(|contribution| contribution.plmc_vesting_info.is_none()); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(contribution) = unscheduled_contributions.next() { - match Pallet::::do_start_contribution_vesting_schedule_for( - &T::PalletId::get().into_account_truncating(), - project_id, - &contribution.contributor, - contribution.id, - ) { - Ok(_) => {}, - Err(e) => { - Pallet::::deposit_event(Event::StartContributionVestingScheduleFailed { - project_id: contribution.project_id, - contributor: contribution.contributor.clone(), - id: contribution.id, - error: e, - }); - }, - } - ( - base_weight.saturating_add(WeightInfoOf::::start_contribution_vesting_schedule_for()), - unscheduled_contributions.count() as u64, - ) - } else { - (base_weight, 0u64) - } -} - -fn mint_ct_for_one_bid(project_id: ProjectId) -> (Weight, u64) { - let project_bids = Bids::::iter_prefix_values((project_id,)); - let mut remaining_bids = project_bids - .filter(|bid| !bid.ct_minted && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(bid) = remaining_bids.next() { - match Pallet::::do_bid_ct_mint_for( - &T::PalletId::get().into_account_truncating(), - bid.project_id, - &bid.bidder, - bid.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::CTMintFailed { - project_id: bid.project_id, - claimer: bid.bidder.clone(), - id: bid.id, - error: e.error, - }), - }; - ( - base_weight.saturating_add(WeightInfoOf::::bid_ct_mint_for_with_ct_account_creation()), - remaining_bids.count() as u64, - ) - } else { - (base_weight, 0u64) - } -} - -fn mint_ct_for_one_contribution(project_id: ProjectId) -> (Weight, u64) { - let project_contributions = Contributions::::iter_prefix_values((project_id,)); - let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.ct_minted); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(contribution) = remaining_contributions.next() { - match Pallet::::do_contribution_ct_mint_for( - &T::PalletId::get().into_account_truncating(), - contribution.project_id, - &contribution.contributor, - contribution.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::CTMintFailed { - project_id: contribution.project_id, - claimer: contribution.contributor.clone(), - id: contribution.id, - error: e.error, - }), - }; - ( - base_weight.saturating_add(WeightInfoOf::::contribution_ct_mint_for_with_ct_account_creation()), - remaining_contributions.count() as u64, - ) - } else { - (base_weight, 0u64) - } -} - -fn issuer_funding_payout_one_bid(project_id: ProjectId) -> (Weight, u64) { - let project_bids = Bids::::iter_prefix_values((project_id,)); - - let mut remaining_bids = project_bids.filter(|bid| { - !bid.funds_released && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) - }); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(bid) = remaining_bids.next() { - match Pallet::::do_payout_bid_funds_for( - &T::PalletId::get().into_account_truncating(), - bid.project_id, - &bid.bidder, - bid.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::PayoutContributionFundsFailed { - project_id: bid.project_id, - contributor: bid.bidder.clone(), - id: bid.id, - error: e, - }), - }; - (base_weight.saturating_add(WeightInfoOf::::payout_bid_funds_for()), remaining_bids.count() as u64) - } else { - (base_weight, 0u64) - } -} - -fn issuer_funding_payout_one_contribution(project_id: ProjectId) -> (Weight, u64) { - let project_contributions = Contributions::::iter_prefix_values((project_id,)); - - let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.funds_released); - let base_weight = Weight::from_parts(10_000_000, 0); - - if let Some(contribution) = remaining_contributions.next() { - match Pallet::::do_payout_contribution_funds_for( - &T::PalletId::get().into_account_truncating(), - contribution.project_id, - &contribution.contributor, - contribution.id, - ) { - Ok(_) => (), - Err(e) => Pallet::::deposit_event(Event::PayoutContributionFundsFailed { - project_id: contribution.project_id, - contributor: contribution.contributor.clone(), - id: contribution.id, - error: e, - }), - }; - - ( - base_weight.saturating_add(WeightInfoOf::::payout_contribution_funds_for()), - remaining_contributions.count() as u64, - ) - } else { - (base_weight, 0u64) - } -} +// use frame_support::{ +// dispatch::{DispatchErrorWithPostInfo, GetDispatchInfo}, +// traits::Get, +// weights::Weight, +// }; +// use sp_arithmetic::traits::Zero; +// use sp_runtime::{traits::AccountIdConversion, DispatchError}; +// use sp_std::marker::PhantomData; + +// use crate::{traits::DoRemainingOperation, *}; + +// impl DoRemainingOperation for Cleaner { +// fn has_remaining_operations(&self) -> bool { +// match self { +// Cleaner::NotReady => false, +// Cleaner::Success(state) => +// as DoRemainingOperation>::has_remaining_operations(state), +// Cleaner::Failure(state) => +// as DoRemainingOperation>::has_remaining_operations(state), +// } +// } + +// fn do_one_operation(&mut self, project_id: ProjectId) -> Result { +// match self { +// Cleaner::NotReady => Err(DispatchError::Other("Cleaner not ready")), +// Cleaner::Success(state) => +// as DoRemainingOperation>::do_one_operation(state, project_id), +// Cleaner::Failure(state) => +// as DoRemainingOperation>::do_one_operation(state, project_id), +// } +// } +// } + +// impl DoRemainingOperation for CleanerState { +// fn has_remaining_operations(&self) -> bool { +// !matches!(self, CleanerState::Finished(_)) +// } + +// fn do_one_operation(&mut self, project_id: ProjectId) -> Result { +// let evaluators_outcome = ProjectsDetails::::get(project_id) +// .ok_or(Error::::ImpossibleState)? +// .evaluation_round_info +// .evaluators_outcome; +// let base_weight = Weight::from_parts(10_000_000, 0); + +// match self { +// CleanerState::Initialized(PhantomData) => { +// *self = Self::EvaluationRewardOrSlash( +// remaining_evaluators_to_reward_or_slash::(project_id, evaluators_outcome), +// PhantomData, +// ); +// Ok(base_weight) +// }, +// CleanerState::EvaluationRewardOrSlash(remaining, PhantomData) => +// if *remaining == 0 { +// *self = Self::EvaluationUnbonding(remaining_evaluations::(project_id), PhantomData); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_evaluations) = +// reward_or_slash_one_evaluation::(project_id).map_err(|error_info| error_info.error)?; +// *self = CleanerState::EvaluationRewardOrSlash(remaining_evaluations, PhantomData); +// Ok(consumed_weight) +// }, +// CleanerState::EvaluationUnbonding(remaining, PhantomData) => +// if *remaining == 0 { +// *self = CleanerState::StartBidderVestingSchedule( +// remaining_successful_bids::(project_id), +// PhantomData, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_evaluations) = unbond_one_evaluation::(project_id); +// *self = CleanerState::EvaluationUnbonding(remaining_evaluations, PhantomData); +// Ok(consumed_weight) +// }, +// CleanerState::StartBidderVestingSchedule(remaining, PhantomData) => +// if *remaining == 0 { +// *self = CleanerState::StartContributorVestingSchedule( +// remaining_contributions::(project_id), +// PhantomData, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_evaluations) = start_one_bid_vesting_schedule::(project_id); +// *self = CleanerState::StartBidderVestingSchedule(remaining_evaluations, PhantomData); +// Ok(consumed_weight) +// }, +// CleanerState::StartContributorVestingSchedule(remaining, PhantomData) => +// if *remaining == 0 { +// *self = CleanerState::BidCTMint(remaining_bids_without_ct_minted::(project_id), PhantomData); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_evaluations) = +// start_one_contribution_vesting_schedule::(project_id); +// *self = CleanerState::StartContributorVestingSchedule(remaining_evaluations, PhantomData); +// Ok(consumed_weight) +// }, +// CleanerState::BidCTMint(remaining, PhantomData) => +// if *remaining == 0 { +// *self = CleanerState::ContributionCTMint( +// remaining_contributions_without_ct_minted::(project_id), +// PhantomData, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_bids) = mint_ct_for_one_bid::(project_id); +// *self = CleanerState::BidCTMint(remaining_bids, PhantomData); +// Ok(consumed_weight) +// }, +// CleanerState::ContributionCTMint(remaining, PhantomData) => +// if *remaining == 0 { +// *self = CleanerState::BidFundingPayout( +// remaining_bids_without_issuer_payout::(project_id), +// PhantomData, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_contributions) = mint_ct_for_one_contribution::(project_id); +// *self = CleanerState::ContributionCTMint(remaining_contributions, PhantomData); +// Ok(consumed_weight) +// }, +// CleanerState::BidFundingPayout(remaining, PhantomData) => +// if *remaining == 0 { +// *self = CleanerState::ContributionFundingPayout( +// remaining_contributions_without_issuer_payout::(project_id), +// PhantomData, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_contributions) = issuer_funding_payout_one_bid::(project_id); +// *self = CleanerState::BidFundingPayout(remaining_contributions, PhantomData); +// Ok(consumed_weight) +// }, +// CleanerState::ContributionFundingPayout(remaining, PhantomData) => +// if *remaining == 0 { +// *self = CleanerState::Finished(PhantomData); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_contributions) = +// issuer_funding_payout_one_contribution::(project_id); +// *self = CleanerState::ContributionFundingPayout(remaining_contributions, PhantomData); +// Ok(consumed_weight) +// }, +// CleanerState::Finished(PhantomData) => Err(Error::::FinalizerFinished.into()), + +// _ => Err(Error::::ImpossibleState.into()), +// } +// } +// } +// impl DoRemainingOperation for CleanerState { +// fn has_remaining_operations(&self) -> bool { +// !matches!(self, CleanerState::Finished(PhantomData::)) +// } + +// fn do_one_operation(&mut self, project_id: ProjectId) -> Result { +// let evaluators_outcome = ProjectsDetails::::get(project_id) +// .ok_or(Error::::ImpossibleState)? +// .evaluation_round_info +// .evaluators_outcome; +// let base_weight = Weight::from_parts(10_000_000, 0); + +// match self { +// CleanerState::Initialized(PhantomData::) => { +// *self = CleanerState::EvaluationRewardOrSlash( +// remaining_evaluators_to_reward_or_slash::(project_id, evaluators_outcome), +// PhantomData::, +// ); +// Ok(base_weight) +// }, + +// CleanerState::EvaluationRewardOrSlash(remaining, PhantomData::) => +// if *remaining == 0 { +// *self = CleanerState::EvaluationUnbonding( +// remaining_evaluations::(project_id), +// PhantomData::, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_evaluators) = +// reward_or_slash_one_evaluation::(project_id).map_err(|error_info| error_info.error)?; +// *self = CleanerState::EvaluationRewardOrSlash(remaining_evaluators, PhantomData); +// Ok(consumed_weight) +// }, + +// CleanerState::EvaluationUnbonding(remaining, PhantomData::) => +// if *remaining == 0 { +// *self = CleanerState::BidFundingRelease( +// remaining_bids_to_release_funds::(project_id), +// PhantomData::, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_evaluators) = unbond_one_evaluation::(project_id); +// *self = CleanerState::EvaluationUnbonding(remaining_evaluators, PhantomData); +// Ok(consumed_weight) +// }, + +// CleanerState::BidFundingRelease(remaining, PhantomData::) => +// if *remaining == 0 { +// *self = CleanerState::BidUnbonding(remaining_bids::(project_id), PhantomData::); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_bids) = release_funds_one_bid::(project_id); +// *self = CleanerState::BidFundingRelease(remaining_bids, PhantomData); +// Ok(consumed_weight) +// }, + +// CleanerState::BidUnbonding(remaining, PhantomData::) => +// if *remaining == 0 { +// *self = CleanerState::ContributionFundingRelease( +// remaining_contributions_to_release_funds::(project_id), +// PhantomData::, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_bids) = unbond_one_bid::(project_id); +// *self = CleanerState::BidUnbonding(remaining_bids, PhantomData::); +// Ok(consumed_weight) +// }, + +// CleanerState::ContributionFundingRelease(remaining, PhantomData::) => +// if *remaining == 0 { +// *self = CleanerState::ContributionUnbonding( +// remaining_contributions::(project_id), +// PhantomData::, +// ); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_contributions) = release_funds_one_contribution::(project_id); +// *self = CleanerState::ContributionFundingRelease(remaining_contributions, PhantomData::); +// Ok(consumed_weight) +// }, + +// CleanerState::ContributionUnbonding(remaining, PhantomData::) => +// if *remaining == 0 { +// *self = CleanerState::Finished(PhantomData::); +// Ok(base_weight) +// } else { +// let (consumed_weight, remaining_contributions) = unbond_one_contribution::(project_id); +// *self = CleanerState::ContributionUnbonding(remaining_contributions, PhantomData::); +// Ok(consumed_weight) +// }, + +// CleanerState::Finished(PhantomData::) => Err(Error::::FinalizerFinished.into()), + +// _ => Err(Error::::ImpossibleState.into()), +// } +// } +// } + +// fn remaining_evaluators_to_reward_or_slash(project_id: ProjectId, outcome: EvaluatorsOutcomeOf) -> u64 { +// if outcome == EvaluatorsOutcomeOf::::Unchanged { +// 0u64 +// } else { +// Evaluations::::iter_prefix_values((project_id,)) +// .filter(|evaluation| evaluation.rewarded_or_slashed.is_none()) +// .count() as u64 +// } +// } + +// fn remaining_evaluations(project_id: ProjectId) -> u64 { +// Evaluations::::iter_prefix_values((project_id,)).count() as u64 +// } + +// fn remaining_bids_to_release_funds(project_id: ProjectId) -> u64 { +// Bids::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 +// } + +// fn remaining_bids(project_id: ProjectId) -> u64 { +// Bids::::iter_prefix_values((project_id,)).count() as u64 +// } + +// fn remaining_successful_bids(project_id: ProjectId) -> u64 { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) +// .count() as u64 +// } + +// fn remaining_contributions_to_release_funds(project_id: ProjectId) -> u64 { +// Contributions::::iter_prefix_values((project_id,)).filter(|contribution| !contribution.funds_released).count() +// as u64 +// } + +// fn remaining_contributions(project_id: ProjectId) -> u64 { +// Contributions::::iter_prefix_values((project_id,)).count() as u64 +// } + +// fn remaining_bids_without_ct_minted(project_id: ProjectId) -> u64 { +// let project_bids = Bids::::iter_prefix_values((project_id,)); +// project_bids.filter(|bid| !bid.ct_minted).count() as u64 +// } + +// fn remaining_contributions_without_ct_minted(project_id: ProjectId) -> u64 { +// let project_contributions = Contributions::::iter_prefix_values((project_id,)); +// project_contributions.filter(|contribution| !contribution.ct_minted).count() as u64 +// } + +// fn remaining_bids_without_issuer_payout(project_id: ProjectId) -> u64 { +// Bids::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 +// } + +// fn remaining_contributions_without_issuer_payout(project_id: ProjectId) -> u64 { +// Contributions::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 +// } + +// fn reward_or_slash_one_evaluation( +// project_id: ProjectId, +// ) -> Result<(Weight, u64), DispatchErrorWithPostInfo> { +// let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectNotFound)?; +// let project_evaluations = Evaluations::::iter_prefix_values((project_id,)); +// let mut remaining_evaluations = project_evaluations.filter(|evaluation| evaluation.rewarded_or_slashed.is_none()); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(evaluation) = remaining_evaluations.next() { +// // TODO: This base weight and the one in all other functions below should be calculated with a benchmark +// let remaining = remaining_evaluations.count() as u64; +// match project_details.evaluation_round_info.evaluators_outcome { +// EvaluatorsOutcome::Rewarded(_) => { +// let mut weight_consumed = crate::Call::::evaluation_reward_payout_for { +// project_id: evaluation.project_id, +// evaluator: evaluation.evaluator.clone(), +// bond_id: evaluation.id, +// } +// .get_dispatch_info() +// .weight; + +// match Pallet::::do_evaluation_reward_payout_for( +// &T::PalletId::get().into_account_truncating(), +// evaluation.project_id, +// &evaluation.evaluator, +// evaluation.id, +// ) { +// Ok(result) => { +// if let Some(weight) = result.actual_weight { +// weight_consumed = weight +// }; +// }, +// Err(e) => { +// if let Some(weight) = e.post_info.actual_weight { +// weight_consumed = weight +// }; +// Pallet::::deposit_event(Event::EvaluationRewardFailed { +// project_id: evaluation.project_id, +// evaluator: evaluation.evaluator.clone(), +// id: evaluation.id, +// error: e.error, +// }) +// }, +// }; + +// Ok((base_weight.saturating_add(weight_consumed), remaining)) +// }, +// EvaluatorsOutcome::Slashed => { +// match Pallet::::do_evaluation_slash_for( +// &T::PalletId::get().into_account_truncating(), +// evaluation.project_id, +// &evaluation.evaluator, +// evaluation.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::EvaluationSlashFailed { +// project_id: evaluation.project_id, +// evaluator: evaluation.evaluator.clone(), +// id: evaluation.id, +// error: e, +// }), +// }; + +// Ok((base_weight.saturating_add(WeightInfoOf::::evaluation_slash_for()), remaining)) +// }, +// _ => { +// #[cfg(debug_assertions)] +// unreachable!("EvaluatorsOutcome should be either Slashed or Rewarded if this function is called"); +// #[cfg(not(debug_assertions))] +// Err(Error::::ImpossibleState.into()) +// }, +// } +// } else { +// Ok((base_weight, 0u64)) +// } +// } + +// fn unbond_one_evaluation(project_id: ProjectId) -> (Weight, u64) { +// let project_evaluations = Evaluations::::iter_prefix_values((project_id,)); +// let mut remaining_evaluations = +// project_evaluations.filter(|evaluation| evaluation.current_plmc_bond > Zero::zero()); +// let base_weight = Weight::from_parts(10_000_000, 0); +// if let Some(evaluation) = remaining_evaluations.next() { +// match Pallet::::do_evaluation_unbond_for( +// &T::PalletId::get().into_account_truncating(), +// evaluation.project_id, +// &evaluation.evaluator, +// evaluation.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::EvaluationUnbondFailed { +// project_id: evaluation.project_id, +// evaluator: evaluation.evaluator.clone(), +// id: evaluation.id, +// error: e, +// }), +// }; +// (base_weight.saturating_add(WeightInfoOf::::evaluation_unbond_for()), remaining_evaluations.count() as u64) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn release_funds_one_bid(project_id: ProjectId) -> (Weight, u64) { +// let project_bids = Bids::::iter_prefix_values((project_id,)); +// let mut remaining_bids = project_bids.filter(|bid| !bid.funds_released); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(bid) = remaining_bids.next() { +// match Pallet::::do_release_bid_funds_for( +// &T::PalletId::get().into_account_truncating(), +// bid.project_id, +// &bid.bidder, +// bid.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::ReleaseBidFundsFailed { +// project_id: bid.project_id, +// bidder: bid.bidder.clone(), +// id: bid.id, +// error: e, +// }), +// }; + +// (base_weight.saturating_add(WeightInfoOf::::release_bid_funds_for()), remaining_bids.count() as u64) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn unbond_one_bid(project_id: ProjectId) -> (Weight, u64) { +// let project_bids = Bids::::iter_prefix_values((project_id,)); +// let mut remaining_bids = project_bids.filter(|bid| bid.funds_released); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(bid) = remaining_bids.next() { +// match Pallet::::do_bid_unbond_for( +// &T::PalletId::get().into_account_truncating(), +// bid.project_id, +// &bid.bidder, +// bid.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::BidUnbondFailed { +// project_id: bid.project_id, +// bidder: bid.bidder.clone(), +// id: bid.id, +// error: e, +// }), +// }; +// (base_weight.saturating_add(WeightInfoOf::::bid_unbond_for()), remaining_bids.count() as u64) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn release_funds_one_contribution(project_id: ProjectId) -> (Weight, u64) { +// let project_contributions = Contributions::::iter_prefix_values((project_id,)); +// let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.funds_released); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(contribution) = remaining_contributions.next() { +// match Pallet::::do_release_contribution_funds_for( +// &T::PalletId::get().into_account_truncating(), +// contribution.project_id, +// &contribution.contributor, +// contribution.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::ReleaseContributionFundsFailed { +// project_id: contribution.project_id, +// contributor: contribution.contributor.clone(), +// id: contribution.id, +// error: e, +// }), +// }; + +// ( +// base_weight.saturating_add(WeightInfoOf::::release_contribution_funds_for()), +// remaining_contributions.count() as u64, +// ) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn unbond_one_contribution(project_id: ProjectId) -> (Weight, u64) { +// let project_contributions = Contributions::::iter_prefix_values((project_id,)); + +// let mut remaining_contributions = +// project_contributions.into_iter().filter(|contribution| contribution.funds_released); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(contribution) = remaining_contributions.next() { +// match Pallet::::do_contribution_unbond_for( +// &T::PalletId::get().into_account_truncating(), +// contribution.project_id, +// &contribution.contributor, +// contribution.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::ContributionUnbondFailed { +// project_id: contribution.project_id, +// contributor: contribution.contributor.clone(), +// id: contribution.id, +// error: e, +// }), +// }; +// ( +// base_weight.saturating_add(WeightInfoOf::::contribution_unbond_for()), +// remaining_contributions.count() as u64, +// ) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn start_one_bid_vesting_schedule(project_id: ProjectId) -> (Weight, u64) { +// let project_bids = Bids::::iter_prefix_values((project_id,)); +// let mut unscheduled_bids = project_bids.filter(|bid| { +// bid.plmc_vesting_info.is_none() && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) +// }); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(bid) = unscheduled_bids.next() { +// match Pallet::::do_start_bid_vesting_schedule_for( +// &T::PalletId::get().into_account_truncating(), +// project_id, +// &bid.bidder, +// bid.id, +// ) { +// Ok(_) => {}, +// Err(e) => { +// // TODO: Handle `MAX_VESTING_SCHEDULES` error + +// Pallet::::deposit_event(Event::StartBidderVestingScheduleFailed { +// project_id: bid.project_id, +// bidder: bid.bidder.clone(), +// id: bid.id, +// error: e, +// }); +// }, +// } +// ( +// base_weight.saturating_add(WeightInfoOf::::start_bid_vesting_schedule_for()), +// unscheduled_bids.count() as u64, +// ) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn start_one_contribution_vesting_schedule(project_id: ProjectId) -> (Weight, u64) { +// let project_bids = Contributions::::iter_prefix_values((project_id,)); +// let mut unscheduled_contributions = project_bids.filter(|contribution| contribution.plmc_vesting_info.is_none()); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(contribution) = unscheduled_contributions.next() { +// match Pallet::::do_start_contribution_vesting_schedule_for( +// &T::PalletId::get().into_account_truncating(), +// project_id, +// &contribution.contributor, +// contribution.id, +// ) { +// Ok(_) => {}, +// Err(e) => { +// Pallet::::deposit_event(Event::StartContributionVestingScheduleFailed { +// project_id: contribution.project_id, +// contributor: contribution.contributor.clone(), +// id: contribution.id, +// error: e, +// }); +// }, +// } +// ( +// base_weight.saturating_add(WeightInfoOf::::start_contribution_vesting_schedule_for()), +// unscheduled_contributions.count() as u64, +// ) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn mint_ct_for_one_bid(project_id: ProjectId) -> (Weight, u64) { +// let project_bids = Bids::::iter_prefix_values((project_id,)); +// let mut remaining_bids = project_bids +// .filter(|bid| !bid.ct_minted && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(bid) = remaining_bids.next() { +// match Pallet::::do_bid_ct_mint_for( +// &T::PalletId::get().into_account_truncating(), +// bid.project_id, +// &bid.bidder, +// bid.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::CTMintFailed { +// project_id: bid.project_id, +// claimer: bid.bidder.clone(), +// id: bid.id, +// error: e.error, +// }), +// }; +// ( +// base_weight.saturating_add(WeightInfoOf::::bid_ct_mint_for_with_ct_account_creation()), +// remaining_bids.count() as u64, +// ) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn mint_ct_for_one_contribution(project_id: ProjectId) -> (Weight, u64) { +// let project_contributions = Contributions::::iter_prefix_values((project_id,)); +// let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.ct_minted); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(contribution) = remaining_contributions.next() { +// match Pallet::::do_contribution_ct_mint_for( +// &T::PalletId::get().into_account_truncating(), +// contribution.project_id, +// &contribution.contributor, +// contribution.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::CTMintFailed { +// project_id: contribution.project_id, +// claimer: contribution.contributor.clone(), +// id: contribution.id, +// error: e.error, +// }), +// }; +// ( +// base_weight.saturating_add(WeightInfoOf::::contribution_ct_mint_for_with_ct_account_creation()), +// remaining_contributions.count() as u64, +// ) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn issuer_funding_payout_one_bid(project_id: ProjectId) -> (Weight, u64) { +// let project_bids = Bids::::iter_prefix_values((project_id,)); + +// let mut remaining_bids = project_bids.filter(|bid| { +// !bid.funds_released && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) +// }); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(bid) = remaining_bids.next() { +// match Pallet::::do_payout_bid_funds_for( +// &T::PalletId::get().into_account_truncating(), +// bid.project_id, +// &bid.bidder, +// bid.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::PayoutContributionFundsFailed { +// project_id: bid.project_id, +// contributor: bid.bidder.clone(), +// id: bid.id, +// error: e, +// }), +// }; +// (base_weight.saturating_add(WeightInfoOf::::payout_bid_funds_for()), remaining_bids.count() as u64) +// } else { +// (base_weight, 0u64) +// } +// } + +// fn issuer_funding_payout_one_contribution(project_id: ProjectId) -> (Weight, u64) { +// let project_contributions = Contributions::::iter_prefix_values((project_id,)); + +// let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.funds_released); +// let base_weight = Weight::from_parts(10_000_000, 0); + +// if let Some(contribution) = remaining_contributions.next() { +// match Pallet::::do_payout_contribution_funds_for( +// &T::PalletId::get().into_account_truncating(), +// contribution.project_id, +// &contribution.contributor, +// contribution.id, +// ) { +// Ok(_) => (), +// Err(e) => Pallet::::deposit_event(Event::PayoutContributionFundsFailed { +// project_id: contribution.project_id, +// contributor: contribution.contributor.clone(), +// id: contribution.id, +// error: e, +// }), +// }; + +// ( +// base_weight.saturating_add(WeightInfoOf::::payout_contribution_funds_for()), +// remaining_contributions.count() as u64, +// ) +// } else { +// (base_weight, 0u64) +// } +// } diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index 2b2f09a85..6439ccb03 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1339,38 +1339,38 @@ impl< let details = self.get_project_details(project_id); self.execute(||{ match details.status { - ProjectStatus::FundingSuccessful => Self::settle_successfull_project(project_id), + ProjectStatus::FundingSuccessful => Self::settle_successful_project(project_id), ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => Self::settle_failed_project(project_id, details.status), _ => panic!("Project should be in FundingSuccessful, FundingFailed or EvaluationFailed status"), } }) } - fn settle_successfull_project(project_id: ProjectId) -> Result<(), DispatchError> { + fn settle_successful_project(project_id: ProjectId) -> Result<(), DispatchError> { Evaluations::::iter_prefix((project_id,)).try_for_each(|(_, evaluation)| { - Pallet::::do_settlement_success_evaluator(evaluation, project_id) + Pallet::::do_settle_successful_evaluation(evaluation, project_id) })?; Bids::::iter_prefix((project_id,)).try_for_each(|(_, bid)| { - Pallet::::do_settlement_success_bidder(bid, project_id) + Pallet::::do_settle_successful_bid(bid, project_id) })?; Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { - Pallet::::do_settlement_success_contributor(contribution, project_id) + Pallet::::do_settle_successful_contribution(contribution, project_id) }) } fn settle_failed_project(project_id: ProjectId, status: ProjectStatus) -> Result<(), DispatchError> { Evaluations::::iter_prefix((project_id,)).try_for_each(|(_, evaluation)| { - Pallet::::do_settlement_failure_evaluator(evaluation, project_id) + Pallet::::do_settle_failed_evaluation(evaluation, project_id) })?; if status == ProjectStatus::FundingFailed { Bids::::iter_prefix((project_id,)).try_for_each(|(_, bid)| { - Pallet::::do_settlement_failure_bidder(bid, project_id) + Pallet::::do_settle_failed_bid(bid, project_id) })?; Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { - Pallet::::do_settlement_failure_contributor(contribution, project_id) + Pallet::::do_settle_failed_contribution(contribution, project_id) })?; } Ok(()) diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 1096c6e95..85a928d66 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -201,7 +201,8 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use local_macros::*; - use sp_arithmetic::Percent; + use pallet_xcm::Origin; +use sp_arithmetic::Percent; use sp_runtime::{ traits::{Convert, ConvertBack, Get}, DispatchErrorWithPostInfo @@ -1133,184 +1134,97 @@ pub mod pallet { Self::do_remaining_contribute(&account, project_id, amount, multiplier, asset, did, investor_type) } - /// Release evaluation-bonded PLMC when a project finishes its funding round. #[pallet::call_index(8)] - #[pallet::weight(WeightInfoOf::::evaluation_unbond_for())] - pub fn evaluation_unbond_for( + #[pallet::weight(WeightInfoOf::::decide_project_outcome( + ::MaxProjectsToUpdateInsertionAttempts::get() - 1 + ))] + pub fn decide_project_outcome( origin: OriginFor, project_id: ProjectId, - evaluator: AccountIdOf, - bond_id: u32, - ) -> DispatchResult { - let releaser = ensure_signed(origin)?; - Self::do_evaluation_unbond_for(&releaser, project_id, &evaluator, bond_id) + outcome: FundingOutcomeDecision, + ) -> DispatchResultWithPostInfo { + let caller = ensure_signed(origin)?; + Self::do_decide_project_outcome(caller, project_id, outcome) } #[pallet::call_index(9)] - #[pallet::weight(WeightInfoOf::::evaluation_slash_for())] - pub fn evaluation_slash_for( + #[pallet::weight(Weight::from_parts(0, 0))] + pub fn settle_successful_evaluation( origin: OriginFor, project_id: ProjectId, evaluator: AccountIdOf, - bond_id: u32, + evaluation_id: u32, ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_evaluation_slash_for(&caller, project_id, &evaluator, bond_id) + let _caller = ensure_signed(origin)?; + let bid = Evaluations::::get((project_id, evaluator, evaluation_id)).ok_or(Error::::BidNotFound)?; + Self::do_settle_successful_evaluation(bid, project_id) } #[pallet::call_index(10)] - #[pallet::weight( - WeightInfoOf::::evaluation_reward_payout_for_with_ct_account_creation() - .max(WeightInfoOf::::evaluation_reward_payout_for_no_ct_account_creation()) - )] - pub fn evaluation_reward_payout_for( - origin: OriginFor, - project_id: ProjectId, - evaluator: AccountIdOf, - bond_id: u32, - ) -> DispatchResultWithPostInfo { - let caller = ensure_signed(origin)?; - Self::do_evaluation_reward_payout_for(&caller, project_id, &evaluator, bond_id) - } - - #[pallet::call_index(11)] - #[pallet::weight( - WeightInfoOf::::bid_ct_mint_for_with_ct_account_creation() - .max(WeightInfoOf::::bid_ct_mint_for_no_ct_account_creation()) - )] - pub fn bid_ct_mint_for( - origin: OriginFor, - project_id: ProjectId, - bidder: AccountIdOf, - bid_id: u32, - ) -> DispatchResultWithPostInfo { - let caller = ensure_signed(origin)?; - Self::do_bid_ct_mint_for(&caller, project_id, &bidder, bid_id) - } - - #[pallet::call_index(12)] - #[pallet::weight( - WeightInfoOf::::contribution_ct_mint_for_with_ct_account_creation() - .max(WeightInfoOf::::contribution_ct_mint_for_no_ct_account_creation()) - )] - pub fn contribution_ct_mint_for( - origin: OriginFor, - project_id: ProjectId, - contributor: AccountIdOf, - contribution_id: u32, - ) -> DispatchResultWithPostInfo { - let caller = ensure_signed(origin)?; - Self::do_contribution_ct_mint_for(&caller, project_id, &contributor, contribution_id) - } - - #[pallet::call_index(13)] - #[pallet::weight(WeightInfoOf::::start_bid_vesting_schedule_for())] - pub fn start_bid_vesting_schedule_for( + #[pallet::weight(Weight::from_parts(0, 0))] + pub fn settle_successful_bid ( origin: OriginFor, project_id: ProjectId, bidder: AccountIdOf, bid_id: u32, ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_start_bid_vesting_schedule_for(&caller, project_id, &bidder, bid_id) + let _caller = ensure_signed(origin)?; + let bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::BidNotFound)?; + Self::do_settle_successful_bid(bid, project_id) } - #[pallet::call_index(14)] - #[pallet::weight(WeightInfoOf::::start_contribution_vesting_schedule_for())] - pub fn start_contribution_vesting_schedule_for( - origin: OriginFor, - project_id: ProjectId, - contributor: AccountIdOf, - contribution_id: u32, - ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_start_contribution_vesting_schedule_for(&caller, project_id, &contributor, contribution_id) - } - - #[pallet::call_index(15)] - #[pallet::weight(WeightInfoOf::::payout_bid_funds_for())] - pub fn payout_bid_funds_for( - origin: OriginFor, - project_id: ProjectId, - bidder: AccountIdOf, - bid_id: u32, - ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_payout_bid_funds_for(&caller, project_id, &bidder, bid_id) - } - - #[pallet::call_index(16)] - #[pallet::weight(WeightInfoOf::::payout_contribution_funds_for())] - pub fn payout_contribution_funds_for( + #[pallet::call_index(11)] + #[pallet::weight(Weight::from_parts(0, 0))] + pub fn settle_successful_contribution( origin: OriginFor, project_id: ProjectId, contributor: AccountIdOf, contribution_id: u32, ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_payout_contribution_funds_for(&caller, project_id, &contributor, contribution_id) + let _caller = ensure_signed(origin)?; + let bid = Contributions::::get((project_id, contributor, contribution_id)).ok_or(Error::::BidNotFound)?; + Self::do_settle_successful_contribution(bid, project_id) } - #[pallet::call_index(17)] - #[pallet::weight(WeightInfoOf::::decide_project_outcome( - ::MaxProjectsToUpdateInsertionAttempts::get() - 1 - ))] - pub fn decide_project_outcome( - origin: OriginFor, - project_id: ProjectId, - outcome: FundingOutcomeDecision, - ) -> DispatchResultWithPostInfo { - let caller = ensure_signed(origin)?; - Self::do_decide_project_outcome(caller, project_id, outcome) - } - - #[pallet::call_index(18)] - #[pallet::weight(WeightInfoOf::::release_bid_funds_for())] - pub fn release_bid_funds_for( + #[pallet::call_index(12)] + #[pallet::weight(Weight::from_parts(0, 0))] + pub fn settle_failed_evaluation( origin: OriginFor, project_id: ProjectId, - bidder: AccountIdOf, - bid_id: u32, + evaluator: AccountIdOf, + evaluation_id: u32, ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_release_bid_funds_for(&caller, project_id, &bidder, bid_id) + let _caller = ensure_signed(origin)?; + let bid = Evaluations::::get((project_id, evaluator, evaluation_id)).ok_or(Error::::BidNotFound)?; + Self::do_settle_failed_evaluation(bid, project_id) } - #[pallet::call_index(19)] - #[pallet::weight(WeightInfoOf::::bid_unbond_for())] - pub fn bid_unbond_for( + #[pallet::call_index(13)] + #[pallet::weight(Weight::from_parts(0, 0))] + pub fn settle_failed_bid ( origin: OriginFor, project_id: ProjectId, bidder: AccountIdOf, bid_id: u32, ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_bid_unbond_for(&caller, project_id, &bidder, bid_id) + let _caller = ensure_signed(origin)?; + let bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::BidNotFound)?; + Self::do_settle_failed_bid(bid, project_id) } - #[pallet::call_index(20)] - #[pallet::weight(WeightInfoOf::::release_contribution_funds_for())] - pub fn release_contribution_funds_for( + #[pallet::call_index(14)] + #[pallet::weight(Weight::from_parts(0, 0))] + pub fn settle_failed_contribution( origin: OriginFor, project_id: ProjectId, contributor: AccountIdOf, contribution_id: u32, ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_release_contribution_funds_for(&caller, project_id, &contributor, contribution_id) + let _caller = ensure_signed(origin)?; + let bid = Contributions::::get((project_id, contributor, contribution_id)).ok_or(Error::::BidNotFound)?; + Self::do_settle_failed_contribution(bid, project_id) } - #[pallet::call_index(21)] - #[pallet::weight(WeightInfoOf::::contribution_unbond_for())] - pub fn contribution_unbond_for( - origin: OriginFor, - project_id: ProjectId, - contributor: AccountIdOf, - contribution_id: u32, - ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_contribution_unbond_for(&caller, project_id, &contributor, contribution_id) - } #[pallet::call_index(22)] #[pallet::weight(Weight::from_parts(1000, 0))] diff --git a/pallets/funding/src/mock.rs b/pallets/funding/src/mock.rs index efc5f8295..ea692fcd2 100644 --- a/pallets/funding/src/mock.rs +++ b/pallets/funding/src/mock.rs @@ -331,7 +331,7 @@ pub struct DummyConverter; impl sp_runtime::traits::Convert for DummyConverter { fn convert(a: AccountId) -> [u8; 32] { let mut account: [u8; 32] = [0u8; 32]; - account[0..7].copy_from_slice(a.to_le_bytes().as_slice()); + account[0..4].copy_from_slice(a.to_le_bytes().as_slice()); account } } diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index 8101ba880..719561457 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -20,7 +20,72 @@ use polimec_common::{ }; impl Pallet { - pub fn do_settlement_success_bidder(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { + pub fn do_settle_successful_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::::NotAllowed); + + // Based on the results of the funding round, the evaluator is either: + // 1. Slashed + // 2. Rewarded with CT tokens + // 3. Not slashed or Rewarded. + let (bond, reward): (BalanceOf, BalanceOf) = match project_details.evaluation_round_info.evaluators_outcome { + EvaluatorsOutcome::Slashed => (Self::slash_evaluator(project_id, &evaluation)?, Zero::zero()), + EvaluatorsOutcome::Rewarded(info) => Self::reward_evaluator(project_id, &evaluation, &info)?, + EvaluatorsOutcome::Unchanged => (evaluation.current_plmc_bond, Zero::zero()), + }; + + // Release the held PLMC bond + T::NativeCurrency::release( + &HoldReason::Evaluation(project_id).into(), + &evaluation.evaluator, + bond, + Precision::Exact, + )?; + + // Create Migration + if reward > Zero::zero() { + let multiplier = MultiplierOf::::try_from(1u8).map_err(|_| Error::::BadMath)?; + let duration = multiplier.calculate_vesting_duration::(); + Self::create_migration(project_id, &evaluation.evaluator, evaluation.id, ParticipationType::Evaluation, reward, duration)?; + } + Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); + + // TODO: Emit an event + + Ok(()) + } + + pub fn do_settle_failed_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!( + matches!(project_details.status, ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed), + Error::::NotAllowed + ); + + let bond; + if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { + bond = Self::slash_evaluator(project_id, &evaluation)?; + } else { + bond = evaluation.current_plmc_bond; + } + + + // Release the held PLMC bond + T::NativeCurrency::release( + &HoldReason::Evaluation(project_id).into(), + &evaluation.evaluator, + bond, + Precision::Exact, + )?; + + Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); + + // TODO: Emit an event + + Ok(()) + } + + pub fn do_settle_successful_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; // Ensure that: @@ -62,7 +127,7 @@ impl Pallet { Ok(()) } - pub fn do_settlement_failure_bidder(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { + pub fn do_settle_failed_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; ensure!( matches!(project_details.status, ProjectStatus::FundingFailed), @@ -88,7 +153,7 @@ impl Pallet { Ok(()) } - pub fn do_settlement_success_contributor(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { + pub fn do_settle_successful_contribution(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; // Ensure that: // 1. The project is in the FundingSuccessful state @@ -125,7 +190,7 @@ impl Pallet { Ok(()) } - pub fn do_settlement_failure_contributor(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { + pub fn do_settle_failed_contribution(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; ensure!( matches!(project_details.status, ProjectStatus::FundingFailed), @@ -151,70 +216,6 @@ impl Pallet { Ok(()) } - pub fn do_settlement_success_evaluator(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::::NotAllowed); - - // Based on the results of the funding round, the evaluator is either: - // 1. Slashed - // 2. Rewarded with CT tokens - // 3. Not slashed or Rewarded. - let (bond, reward): (BalanceOf, BalanceOf) = match project_details.evaluation_round_info.evaluators_outcome { - EvaluatorsOutcome::Slashed => (Self::slash_evaluator(project_id, &evaluation)?, Zero::zero()), - EvaluatorsOutcome::Rewarded(info) => Self::reward_evaluator(project_id, &evaluation, &info)?, - EvaluatorsOutcome::Unchanged => (evaluation.current_plmc_bond, Zero::zero()), - }; - - // Release the held PLMC bond - T::NativeCurrency::release( - &HoldReason::Evaluation(project_id).into(), - &evaluation.evaluator, - bond, - Precision::Exact, - )?; - - // Create Migration - if reward > Zero::zero() { - let multiplier = MultiplierOf::::try_from(1u8).map_err(|_| Error::::BadMath)?; - let duration = multiplier.calculate_vesting_duration::(); - Self::create_migration(project_id, &evaluation.evaluator, evaluation.id, ParticipationType::Evaluation, reward, duration)?; - } - Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); - - // TODO: Emit an event - - Ok(()) - } - - pub fn do_settlement_failure_evaluator(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!( - matches!(project_details.status, ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed), - Error::::NotAllowed - ); - - let bond; - if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { - bond = Self::slash_evaluator(project_id, &evaluation)?; - } else { - bond = evaluation.current_plmc_bond; - } - - - // Release the held PLMC bond - T::NativeCurrency::release( - &HoldReason::Evaluation(project_id).into(), - &evaluation.evaluator, - bond, - Precision::Exact, - )?; - - Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); - - // TODO: Emit an event - - Ok(()) - } fn mint_ct_tokens(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { if !T::ContributionTokenCurrency::contains(&project_id, participant) { diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index 752875109..5b4bee08e 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -3527,2115 +3527,2115 @@ mod remainder_contribution { } // only functionalities that happen after the REMAINDER FUNDING period of a project, and before the CT Migration -mod funding_end { - use super::*; - - #[test] - fn automatic_fail_less_eq_33_percent() { - for funding_percent in (1..=33).step_by(5) { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); - let min_price = project_metadata.minimum_price; - let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * - (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - vec![100u8], - vec![BIDDER_1], - vec![10u8], - ); - let contributions = MockInstantiator::generate_contributions_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let project_id = - inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - } - } - - #[test] - fn automatic_success_bigger_eq_90_percent() { - for funding_percent in (90..=100).step_by(2) { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); - let min_price = project_metadata.minimum_price; - let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * - (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let contributions = MockInstantiator::generate_contributions_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let project_id = - inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - } - } - - #[test] - fn manual_outcome_above33_to_below90() { - for funding_percent in (34..90).step_by(5) { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); - let min_price = project_metadata.minimum_price; - let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * - (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let contributions = MockInstantiator::generate_contributions_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let project_id = - inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - } - } - - #[test] - fn manual_acceptance() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); - let min_price = project_metadata.minimum_price; - let twenty_percent_funding_usd = Perquintill::from_percent(55) * - (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let contributions = MockInstantiator::generate_contributions_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let project_id = - inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - - let project_id = project_id; - inst.execute(|| { - PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::AcceptFunding) - }) - .unwrap(); - - inst.advance_time(1u64).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - - assert_matches!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Initialized(_))); - inst.test_ct_created_for(project_id); - - inst.advance_time(10u64).unwrap(); - assert_matches!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Finished(PhantomData)) - ); - } - - #[test] - fn manual_rejection() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); - let min_price = project_metadata.minimum_price; - let twenty_percent_funding_usd = Perquintill::from_percent(55) * - (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let contributions = MockInstantiator::generate_contributions_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let project_id = - inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - - let project_id = project_id; - inst.execute(|| { - PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::RejectFunding) - }) - .unwrap(); - - inst.advance_time(1u64).unwrap(); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_matches!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Initialized(PhantomData)) - ); - - inst.test_ct_not_created_for(project_id); - - inst.advance_time(10u64).unwrap(); - assert_matches!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Finished(PhantomData)) - ); - } - - #[test] - fn automatic_acceptance_on_manual_decision_after_time_delta() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); - let min_price = project_metadata.minimum_price; - let twenty_percent_funding_usd = Perquintill::from_percent(55) * - (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let contributions = MockInstantiator::generate_contributions_from_total_usd( - Percent::from_percent(50u8) * twenty_percent_funding_usd, - min_price, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let project_id = - inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - - let project_id = project_id; - inst.advance_time(1u64 + ::ManualAcceptanceDuration::get()).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - - inst.test_ct_created_for(project_id); - - inst.settle_project(project_id).unwrap(); - } - - #[test] - fn evaluators_get_slashed_funding_accepted() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = project_from_funding_reached(&mut inst, 43u64); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - - let old_evaluation_locked_plmc = inst - .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) - .into_iter() - .filter(|item| item.plmc_amount > Zero::zero()) - .collect::>>(); - - let evaluators = old_evaluation_locked_plmc.accounts(); - - let old_participation_locked_plmc = - inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); - let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); - - call_and_is_ok!( - inst, - PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::AcceptFunding) - ); - inst.advance_time(1u64).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); - assert_matches!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Finished(PhantomData)) - ); - - let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); - let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( - vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], - MergeOperation::Add, - ); - - let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); - - assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); - } - - #[test] - fn evaluators_get_slashed_funding_funding_rejected() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = project_from_funding_reached(&mut inst, 56u64); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - - let old_evaluation_locked_plmc = inst - .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) - .into_iter() - .filter(|item| item.plmc_amount > Zero::zero()) - .collect::>>(); - - let evaluators = old_evaluation_locked_plmc.accounts(); - - let old_participation_locked_plmc = - inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); - let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); - - call_and_is_ok!( - inst, - PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::RejectFunding) - ); - inst.advance_time(1u64).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); - assert_matches!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Finished(PhantomData)) - ); - - let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); - let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( - vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], - MergeOperation::Add, - ); - let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); - - assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); - } - - #[test] - fn evaluators_get_slashed_funding_failed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = project_from_funding_reached(&mut inst, 24u64); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - - let old_evaluation_locked_plmc = inst - .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) - .into_iter() - .filter(|item| item.plmc_amount > Zero::zero()) - .collect::>(); - - let evaluators = old_evaluation_locked_plmc.accounts(); - - let old_participation_locked_plmc = - inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); - let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); - - inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); - assert_matches!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Finished(PhantomData)) - ); - - let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); - let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( - vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], - MergeOperation::Add, - ); - - let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); - - assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); - } - - #[test] - fn ct_minted_automatically() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations.clone(), - bids.clone(), - community_contributions.clone(), - remainder_contributions.clone(), - ); - let details = inst.get_project_details(project_id); - assert_eq!(details.status, ProjectStatus::FundingSuccessful); - - let reward_info = match details.evaluation_round_info.evaluators_outcome { - EvaluatorsOutcome::Rewarded(reward_info) => Some(reward_info), - _ => None, - }; - - let evaluators = evaluations.accounts(); - let evaluator_ct_amounts = evaluators - .iter() - .map(|account| { - let evaluations = inst.execute(|| { - Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - let total_evaluator_ct_rewarded = match reward_info.clone() { - Some(info) => { - evaluations - .iter() - .map(|evaluation| { - Pallet::::calculate_evaluator_reward(evaluation, &info) - }) - .sum::() - }, - None => Zero::zero() - }; - - (account, total_evaluator_ct_rewarded) - }) - .collect_vec(); - - let bidders = bids.accounts(); - let bidder_ct_amounts = bidders - .iter() - .map(|account| { - let bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); - - (account, total_bidder_ct_rewarded) - }) - .collect_vec(); - - let community_accounts = community_contributions.accounts(); - let remainder_accounts = remainder_contributions.accounts(); - let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); - let contributor_ct_amounts = all_contributors - .map(|account| { - let contributions = inst.execute(|| { - Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - let total_contributor_ct_rewarded = - contributions.iter().map(|contribution| contribution.ct_amount).sum::(); - - (account, total_contributor_ct_rewarded) - }) - .collect_vec(); - - let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( - vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], - |item| item.0, - Zero::zero(), - |item, accumulator| accumulator + item.1, - ); - - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - - inst.settle_project(project_id).unwrap(); - - for (account, amount) in all_ct_expectations { - let minted = - inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); - assert_eq!(minted, amount); - } - } - - #[test] - fn ct_minted_manually() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations.clone(), - bids.clone(), - community_contributions.clone(), - remainder_contributions.clone(), - ); - let details = inst.get_project_details(project_id); - assert_eq!(details.status, ProjectStatus::FundingSuccessful); - assert_eq!(details.cleanup, Cleaner::NotReady); - // do_end_funding - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let evaluators = evaluations.accounts(); - let evaluator_ct_amounts = evaluators - .iter() - .map(|account| { - let evaluations = inst.execute(|| { - Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for evaluation in evaluations.iter() { - inst.execute(|| { - assert_ok!(Pallet::::evaluation_reward_payout_for( - RuntimeOrigin::signed(evaluation.evaluator), - project_id, - evaluation.evaluator, - evaluation.id, - )); - }); - } - let evaluations = inst.execute(|| { - Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - let total_evaluator_ct_rewarded = evaluations - .iter() - .map(|evaluation| evaluation.rewarded_or_slashed) - .map(|reward_or_slash| { - if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { - balance - } else { - Zero::zero() - } - }) - .sum::(); - - (account, total_evaluator_ct_rewarded) - }) - .collect_vec(); - - let bidders = bids.accounts(); - let bidder_ct_amounts = bidders - .iter() - .map(|account| { - let bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for bid in bids.iter() { - inst.execute(|| { - assert_ok!(Pallet::::bid_ct_mint_for( - RuntimeOrigin::signed(bid.bidder), - project_id, - bid.bidder, - bid.id, - )); - }); - } - - let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); - - (account, total_bidder_ct_rewarded) - }) - .collect_vec(); - - let community_accounts = community_contributions.accounts(); - let remainder_accounts = remainder_contributions.accounts(); - let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); - let contributor_ct_amounts = all_contributors - .map(|account| { - let contributions = inst.execute(|| { - Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for contribution in contributions.iter() { - inst.execute(|| { - assert_ok!(Pallet::::contribution_ct_mint_for( - RuntimeOrigin::signed(contribution.contributor), - project_id, - contribution.contributor, - contribution.id, - )); - }); - } - - let total_contributor_ct_rewarded = - contributions.iter().map(|contribution| contribution.ct_amount).sum::(); - - (account, total_contributor_ct_rewarded) - }) - .collect_vec(); - - let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( - vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], - |item| item.0, - Zero::zero(), - |item, accumulator| accumulator + item.1, - ); - - for (account, amount) in all_ct_expectations { - let minted = - inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); - assert_eq!(minted, amount, "Account: {}", account); - } - - let details = inst.get_project_details(project_id); - assert_eq!(details.status, ProjectStatus::FundingSuccessful); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); - } - - #[test] - fn cannot_mint_ct_twice_manually() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations.clone(), - bids.clone(), - community_contributions.clone(), - remainder_contributions.clone(), - ); - let details = inst.get_project_details(project_id); - assert_eq!(details.status, ProjectStatus::FundingSuccessful); - assert_eq!(details.cleanup, Cleaner::NotReady); - // do_end_funding - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - - let evaluators = evaluations.accounts(); - let evaluator_ct_amounts = evaluators - .iter() - .map(|account| { - let evaluations = inst.execute(|| { - Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for evaluation in evaluations.iter() { - inst.execute(|| { - assert_ok!(Pallet::::evaluation_reward_payout_for( - RuntimeOrigin::signed(evaluation.evaluator), - project_id, - evaluation.evaluator, - evaluation.id, - )); - assert_noop!( - Pallet::::evaluation_reward_payout_for( - RuntimeOrigin::signed(evaluation.evaluator), - project_id, - evaluation.evaluator, - evaluation.id, - ), - Error::::NotAllowed - ); - }); - } - let evaluations = inst.execute(|| { - Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - let total_evaluator_ct_rewarded = evaluations - .iter() - .map(|evaluation| evaluation.rewarded_or_slashed) - .map(|reward_or_slash| { - if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { - balance - } else { - Zero::zero() - } - }) - .sum::(); - - (account, total_evaluator_ct_rewarded) - }) - .collect_vec(); - - let bidders = bids.accounts(); - let bidder_ct_amounts = bidders - .iter() - .map(|account| { - let bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for bid in bids.iter() { - inst.execute(|| { - assert_ok!(Pallet::::bid_ct_mint_for( - RuntimeOrigin::signed(bid.bidder), - project_id, - bid.bidder, - bid.id, - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::bid_ct_mint_for( - RuntimeOrigin::signed(bid.bidder), - project_id, - bid.bidder, - bid.id, - ), - Error::::NotAllowed - ); - }); - } - - let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); - - (account, total_bidder_ct_rewarded) - }) - .collect_vec(); - - let community_accounts = community_contributions.accounts(); - let remainder_accounts = remainder_contributions.accounts(); - let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); - let contributor_ct_amounts = all_contributors - .map(|account| { - let contributions = inst.execute(|| { - Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for contribution in contributions.iter() { - inst.execute(|| { - assert_ok!(Pallet::::contribution_ct_mint_for( - RuntimeOrigin::signed(contribution.contributor), - project_id, - contribution.contributor, - contribution.id, - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::contribution_ct_mint_for( - RuntimeOrigin::signed(contribution.contributor), - project_id, - contribution.contributor, - contribution.id, - ), - Error::::NotAllowed - ); - }); - } - - let total_contributor_ct_rewarded = - contributions.iter().map(|contribution| contribution.ct_amount).sum::(); - - (account, total_contributor_ct_rewarded) - }) - .collect_vec(); - - let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( - vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], - |item| item.0, - Zero::zero(), - |item, accumulator| accumulator + item.1, - ); - - for (account, amount) in all_ct_expectations { - let minted = - inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); - assert_eq!(minted, amount, "Account: {}", account); - } - - let details = inst.get_project_details(project_id); - assert_eq!(details.status, ProjectStatus::FundingSuccessful); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); - } - - #[test] - fn cannot_mint_ct_manually_after_automatic_mint() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations.clone(), - bids.clone(), - community_contributions.clone(), - remainder_contributions.clone(), - ); - let details = inst.get_project_details(project_id); - assert_eq!(details.status, ProjectStatus::FundingSuccessful); - assert_eq!(details.cleanup, Cleaner::NotReady); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - inst.advance_time(1).unwrap(); - assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - - let evaluators = evaluations.accounts(); - let evaluator_ct_amounts = evaluators - .iter() - .map(|account| { - let evaluations = inst.execute(|| { - Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for evaluation in evaluations.iter() { - inst.execute(|| { - assert_noop!( - Pallet::::evaluation_reward_payout_for( - RuntimeOrigin::signed(evaluation.evaluator), - project_id, - evaluation.evaluator, - evaluation.id, - ), - Error::::NotAllowed - ); - }); - } - let evaluations = inst.execute(|| { - Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - let total_evaluator_ct_rewarded = evaluations - .iter() - .map(|evaluation| evaluation.rewarded_or_slashed) - .map(|reward_or_slash| { - if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { - balance - } else { - Zero::zero() - } - }) - .sum::(); - - (account, total_evaluator_ct_rewarded) - }) - .collect_vec(); - - let bidders = bids.accounts(); - let bidder_ct_amounts = bidders - .iter() - .map(|account| { - let bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for bid in bids.iter() { - inst.execute(|| { - assert_noop!( - Pallet::::bid_ct_mint_for( - RuntimeOrigin::signed(bid.bidder), - project_id, - bid.bidder, - bid.id, - ), - Error::::NotAllowed - ); - }); - } - - let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); - - (account, total_bidder_ct_rewarded) - }) - .collect_vec(); - - let community_accounts = community_contributions.accounts(); - let remainder_accounts = remainder_contributions.accounts(); - let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); - let contributor_ct_amounts = all_contributors - .map(|account| { - let contributions = inst.execute(|| { - Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() - }); - for contribution in contributions.iter() { - inst.execute(|| { - assert_noop!( - Pallet::::contribution_ct_mint_for( - RuntimeOrigin::signed(contribution.contributor), - project_id, - contribution.contributor, - contribution.id, - ), - Error::::NotAllowed - ); - }); - } - - let total_contributor_ct_rewarded = - contributions.iter().map(|contribution| contribution.ct_amount).sum::(); - - (account, total_contributor_ct_rewarded) - }) - .collect_vec(); - - let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( - vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], - |item| item.0, - Zero::zero(), - |item, accumulator| accumulator + item.1, - ); - - for (account, amount) in all_ct_expectations { - let minted = - inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); - assert_eq!(minted, amount, "Account: {}", account); - } - } - - #[test] - fn multiplier_gets_correct_vesting_duration() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = vec![ - BidParams::new(BIDDER_1, 325_000 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_2, 75_000 * ASSET_UNIT, 2u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_3, 50_000 * ASSET_UNIT, 3u8, AcceptedFundingAsset::USDT), - ]; - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids, - community_contributions, - remainder_contributions, - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - - inst.advance_time(10u64).unwrap(); - let details = inst.get_project_details(project_id); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - - let mut stored_bids = - inst.execute(|| Bids::::iter_prefix_values((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, - FixedU128::from_rational(2167, 1000).saturating_mul_int(one_week_in_blocks as u64) - ); - assert_eq!( - stored_bids[2].plmc_vesting_info.unwrap().duration, - FixedU128::from_rational(4334, 1000).saturating_mul_int(one_week_in_blocks as u64) - ); - } - - #[test] - fn funding_assets_are_paid_manually_to_issuer() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids, - community_contributions, - remainder_contributions, - ); - - let final_winning_bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) - .collect::>() - }); - let final_bid_payouts = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) - .map(|bid| { - UserToForeignAssets::new( - bid.bidder, - bid.funding_asset_amount_locked, - bid.funding_asset.to_assethub_id(), - ) - }) - .collect::>>() - }); - let final_contributions = - inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - let final_contribution_payouts = inst.execute(|| { - Contributions::::iter_prefix_values((project_id,)) - .map(|contribution| { - UserToForeignAssets::new( - contribution.contributor, - contribution.funding_asset_amount, - contribution.funding_asset.to_assethub_id(), - ) - }) - .collect::>>() - }); - - let total_expected_bid_payout = - final_bid_payouts.iter().map(|bid| bid.asset_amount).sum::>(); - let total_expected_contribution_payout = final_contribution_payouts - .iter() - .map(|contribution| contribution.asset_amount) - .sum::>(); - - let prev_issuer_funding_balance = - inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - - let prev_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( - final_bid_payouts[0].asset_id, - vec![Pallet::::fund_account_id(project_id)], - )[0] - .asset_amount; - - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - for bid in final_winning_bids { - inst.execute(|| { - Pallet::::payout_bid_funds_for( - RuntimeOrigin::signed(issuer), - project_id, - bid.bidder, - bid.id, - ) - }) - .unwrap(); - } - for contribution in final_contributions { - inst.execute(|| { - Pallet::::payout_contribution_funds_for( - RuntimeOrigin::signed(issuer), - project_id, - contribution.contributor, - contribution.id, - ) - }) - .unwrap(); - } - let post_issuer_funding_balance = - inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - - let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( - final_bid_payouts[0].asset_id, - vec![Pallet::::fund_account_id(project_id)], - )[0] - .asset_amount; - let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - let project_pot_funding_delta = prev_project_pot_funding_balance - post_project_pot_funding_balance; - - assert_eq!(issuer_funding_delta - total_expected_bid_payout, total_expected_contribution_payout); - assert_eq!(issuer_funding_delta, project_pot_funding_delta); - - assert_eq!(post_project_pot_funding_balance, 0u128); - } - - #[test] - fn funding_assets_are_paid_automatically_to_issuer() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids, - community_contributions, - remainder_contributions, - ); - - let final_bid_payouts = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) - .map(|bid| { - UserToForeignAssets::new( - bid.bidder, - bid.funding_asset_amount_locked, - bid.funding_asset.to_assethub_id(), - ) - }) - .collect::>>() - }); - let final_contribution_payouts = inst.execute(|| { - Contributions::::iter_prefix_values((project_id,)) - .map(|contribution| { - UserToForeignAssets::new( - contribution.contributor, - contribution.funding_asset_amount, - contribution.funding_asset.to_assethub_id(), - ) - }) - .collect::>>() - }); - - let total_expected_bid_payout = - final_bid_payouts.iter().map(|bid| bid.asset_amount).sum::>(); - let total_expected_contribution_payout = final_contribution_payouts - .iter() - .map(|contribution| contribution.asset_amount) - .sum::>(); - - let prev_issuer_funding_balance = - inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - - let prev_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( - final_bid_payouts[0].asset_id, - vec![Pallet::::fund_account_id(project_id)], - )[0] - .asset_amount; - - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Success(CleanerState::Initialized(PhantomData)) - ); - inst.advance_time(1u64).unwrap(); - assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - - let post_issuer_funding_balance = - inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - - let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( - final_bid_payouts[0].asset_id, - vec![Pallet::::fund_account_id(project_id)], - )[0] - .asset_amount; - let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - let project_pot_funding_delta = prev_project_pot_funding_balance - post_project_pot_funding_balance; - - assert_eq!(issuer_funding_delta - total_expected_bid_payout, total_expected_contribution_payout); - assert_eq!(issuer_funding_delta, project_pot_funding_delta); - - assert_eq!(post_project_pot_funding_balance, 0u128); - } - - #[test] - fn funding_assets_are_released_automatically_on_funding_fail() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - - let auction_allocation = - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - project_metadata.minimum_price.saturating_mul_int(auction_allocation), - project_metadata.minimum_price, - default_weights(), - default_bidders(), - default_bidder_multipliers(), - ); - let community_contributions = MockInstantiator::generate_contributions_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, - ), - project_metadata.minimum_price, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ); - let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, - ), - project_metadata.minimum_price, - default_weights(), - default_remainder_contributors(), - default_remainder_contributor_multipliers(), - ); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids, - community_contributions.clone(), - remainder_contributions.clone(), - ); - - let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let expected_bid_payouts = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .map(|bid| { - UserToForeignAssets::::new( - bid.bidder, - bid.funding_asset_amount_locked, - bid.funding_asset.to_assethub_id(), - ) - }) - .sorted_by_key(|bid| bid.account) - .collect::>>() - }); - let expected_community_contribution_payouts = - MockInstantiator::calculate_contributed_funding_asset_spent(community_contributions, final_price); - let expected_remainder_contribution_payouts = - MockInstantiator::calculate_contributed_funding_asset_spent(remainder_contributions, final_price); - let all_expected_payouts = MockInstantiator::generic_map_operation( - vec![ - expected_bid_payouts.clone(), - expected_community_contribution_payouts, - expected_remainder_contribution_payouts, - ], - MergeOperation::Add, - ); - - let prev_issuer_funding_balance = - inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - let all_participants = all_expected_payouts.accounts(); - let prev_participants_funding_balances = - inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants.clone()); - - call_and_is_ok!( - inst, - Pallet::::decide_project_outcome( - RuntimeOrigin::signed(issuer), - project_id, - FundingOutcomeDecision::RejectFunding - ) - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.advance_time(10).unwrap(); - assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Failure(CleanerState::Finished(PhantomData))); - - let post_issuer_funding_balance = - inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - let post_participants_funding_balances = - inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants); - let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( - expected_bid_payouts[0].asset_id, - vec![Pallet::::fund_account_id(project_id)], - )[0] - .asset_amount; - - let all_participants_funding_delta = MockInstantiator::generic_map_operation( - vec![prev_participants_funding_balances, post_participants_funding_balances], - MergeOperation::Add, - ); - - let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - - assert_eq!(issuer_funding_delta, 0); - assert_eq!(post_project_pot_funding_balance, 0u128); - assert_eq!(all_expected_payouts, all_participants_funding_delta); - } - - #[test] - fn funding_assets_are_released_manually_on_funding_fail() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let auction_allocation = - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - project_metadata.minimum_price.saturating_mul_int(auction_allocation), - project_metadata.minimum_price, - default_weights(), - default_bidders(), - default_bidder_multipliers(), - ); - let community_contributions = MockInstantiator::generate_contributions_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, - ), - project_metadata.minimum_price, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ); - let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, - ), - project_metadata.minimum_price, - default_weights(), - default_remainder_contributors(), - default_remainder_contributor_multipliers(), - ); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids, - community_contributions.clone(), - remainder_contributions.clone(), - ); - let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let expected_bid_payouts = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .map(|bid| { - UserToForeignAssets::::new( - bid.bidder, - bid.funding_asset_amount_locked, - bid.funding_asset.to_assethub_id(), - ) - }) - .sorted_by_key(|item| item.account) - .collect::>>() - }); - let expected_community_contribution_payouts = - MockInstantiator::calculate_contributed_funding_asset_spent(community_contributions, final_price); - let expected_remainder_contribution_payouts = - MockInstantiator::calculate_contributed_funding_asset_spent(remainder_contributions, final_price); - let all_expected_payouts = MockInstantiator::generic_map_operation( - vec![ - expected_bid_payouts.clone(), - expected_community_contribution_payouts, - expected_remainder_contribution_payouts, - ], - MergeOperation::Add, - ); - - let prev_issuer_funding_balance = - inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - let all_participants = all_expected_payouts.accounts(); - let prev_participants_funding_balances = - inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants.clone()); - - call_and_is_ok!( - inst, - Pallet::::decide_project_outcome( - RuntimeOrigin::signed(issuer), - project_id, - FundingOutcomeDecision::RejectFunding - ) - ); - - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - - let stored_bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) - .collect::>() - }); - let stored_contributions = - inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - - for bid in stored_bids { - call_and_is_ok!( - inst, - Pallet::::release_bid_funds_for( - RuntimeOrigin::signed(issuer), - project_id, - bid.bidder, - bid.id, - ) - ) - } - - for contribution in stored_contributions { - call_and_is_ok!( - inst, - Pallet::::release_contribution_funds_for( - RuntimeOrigin::signed(issuer), - project_id, - contribution.contributor, - contribution.id, - ) - ) - } - - let post_issuer_funding_balance = - inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - let post_participants_funding_balances = - inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants); - let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( - expected_bid_payouts[0].asset_id, - vec![Pallet::::fund_account_id(project_id)], - )[0] - .asset_amount; - - let all_participants_funding_delta = MockInstantiator::generic_map_operation( - vec![prev_participants_funding_balances, post_participants_funding_balances], - MergeOperation::Add, - ); - - let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - - assert_eq!(issuer_funding_delta, 0); - assert_eq!(post_project_pot_funding_balance, 0u128); - assert_eq!(all_expected_payouts, all_participants_funding_delta); - } - - #[test] - fn plmc_bonded_is_returned_automatically_on_funding_fail() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let auction_allocation = - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - project_metadata.minimum_price.saturating_mul_int(auction_allocation), - project_metadata.minimum_price, - default_weights(), - default_bidders(), - default_bidder_multipliers(), - ); - let community_contributions = MockInstantiator::generate_contributions_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, - ), - project_metadata.minimum_price, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ); - let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, - ), - project_metadata.minimum_price, - default_weights(), - default_remainder_contributors(), - default_remainder_contributor_multipliers(), - ); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations.clone(), - bids.clone(), - community_contributions.clone(), - remainder_contributions.clone(), - ); - let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let expected_evaluators_and_contributors_payouts = - MockInstantiator::calculate_total_plmc_locked_from_evaluations_and_remainder_contributions( - evaluations.clone(), - remainder_contributions.clone(), - final_price, - true, - ); - let expected_bid_payouts = - MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, final_price); - let expected_community_contribution_payouts = - MockInstantiator::calculate_contributed_plmc_spent(community_contributions.clone(), final_price); - let all_expected_payouts = MockInstantiator::generic_map_operation( - vec![ - expected_evaluators_and_contributors_payouts.clone(), - expected_bid_payouts, - expected_community_contribution_payouts, - ], - MergeOperation::Add, - ); - println!("all expected payouts {:?}", all_expected_payouts); - for payout in all_expected_payouts.clone() { - let evaluation_hold = inst.execute(|| { - <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( - &HoldReason::Evaluation(project_id).into(), - &payout.account, - ) - }); - let participation_hold = inst.execute(|| { - <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( - &HoldReason::Participation(project_id).into(), - &payout.account, - ) - }); - println!("account {:?} has evaluation hold {:?}", payout.account, evaluation_hold); - println!("account {:?} has participation hold {:?}", payout.account, participation_hold); - } - let deposit_required = <::ContributionTokenCurrency as AccountTouch< - ProjectId, - AccountIdOf, - >>::deposit_required(project_id); - let all_expected_payouts = all_expected_payouts - .into_iter() - .map(|UserToPLMCBalance { account, plmc_amount }| { - UserToPLMCBalance::new(account, plmc_amount + deposit_required) - }) - .collect::>(); - - let prev_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; - - let all_participants = all_expected_payouts.accounts(); - let prev_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants.clone()); - - call_and_is_ok!( - inst, - Pallet::::decide_project_outcome( - RuntimeOrigin::signed(issuer), - project_id, - FundingOutcomeDecision::RejectFunding - ) - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.advance_time(10).unwrap(); - assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Failure(CleanerState::Finished(PhantomData))); - - let post_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; - let post_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants); - - let all_participants_plmc_deltas = MockInstantiator::generic_map_operation( - vec![post_participants_plmc_balances, prev_participants_plmc_balances], - MergeOperation::Subtract, - ); - - let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - - assert_eq!(issuer_funding_delta, 0); - assert_eq!(all_participants_plmc_deltas, all_expected_payouts); - } - - #[test] - fn plmc_bonded_is_returned_manually_on_funding_fail() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let auction_allocation = - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; - let evaluations = default_evaluations(); - let bids = MockInstantiator::generate_bids_from_total_usd( - project_metadata.minimum_price.saturating_mul_int(auction_allocation), - project_metadata.minimum_price, - default_weights(), - default_bidders(), - default_bidder_multipliers(), - ); - let community_contributions = MockInstantiator::generate_contributions_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, - ), - project_metadata.minimum_price, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ); - let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, - ), - project_metadata.minimum_price, - default_weights(), - default_remainder_contributors(), - default_remainder_contributor_multipliers(), - ); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations.clone(), - bids.clone(), - community_contributions.clone(), - remainder_contributions.clone(), - ); - let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let expected_evaluators_and_contributors_payouts = - MockInstantiator::calculate_total_plmc_locked_from_evaluations_and_remainder_contributions( - evaluations.clone(), - remainder_contributions.clone(), - final_price, - true, - ); - let expected_bid_payouts = - MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, final_price); - let expected_community_contribution_payouts = - MockInstantiator::calculate_contributed_plmc_spent(community_contributions.clone(), final_price); - let all_expected_payouts = MockInstantiator::generic_map_operation( - vec![ - expected_evaluators_and_contributors_payouts.clone(), - expected_bid_payouts, - expected_community_contribution_payouts, - ], - MergeOperation::Add, - ); - println!("all expected payouts {:?}", all_expected_payouts); - for payout in all_expected_payouts.clone() { - let evaluation_hold = inst.execute(|| { - <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( - &HoldReason::Evaluation(project_id).into(), - &payout.account, - ) - }); - let participation_hold = inst.execute(|| { - <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( - &HoldReason::Participation(project_id).into(), - &payout.account, - ) - }); - println!("account {:?} has evaluation hold {:?}", payout.account, evaluation_hold); - println!("account {:?} has participation hold {:?}", payout.account, participation_hold); - } - let _deposit_required = <::ContributionTokenCurrency as AccountTouch< - ProjectId, - AccountIdOf, - >>::deposit_required(project_id); - - let prev_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; - let all_participants = all_expected_payouts.accounts(); - let prev_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants.clone()); - - call_and_is_ok!( - inst, - Pallet::::decide_project_outcome( - RuntimeOrigin::signed(issuer), - project_id, - FundingOutcomeDecision::RejectFunding - ) - ); - inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Initialized(PhantomData)) - ); - - let stored_evaluations = - inst.execute(|| Evaluations::::iter_prefix_values((project_id,)).collect::>()); - let stored_bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) - .collect::>() - }); - let stored_contributions = - inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - - for evaluation in stored_evaluations { - call_and_is_ok!( - inst, - Pallet::::evaluation_slash_for( - RuntimeOrigin::signed(evaluation.evaluator), - project_id, - evaluation.evaluator, - evaluation.id, - ), - Pallet::::evaluation_unbond_for( - RuntimeOrigin::signed(evaluation.evaluator), - project_id, - evaluation.evaluator, - evaluation.id, - ) - ) - } - - for bid in stored_bids { - call_and_is_ok!( - inst, - Pallet::::release_bid_funds_for( - RuntimeOrigin::signed(issuer), - project_id, - bid.bidder, - bid.id, - ), - Pallet::::bid_unbond_for( - RuntimeOrigin::signed(bid.bidder), - project_id, - bid.bidder, - bid.id, - ) - ) - } - - for contribution in stored_contributions { - call_and_is_ok!( - inst, - Pallet::::release_contribution_funds_for( - RuntimeOrigin::signed(issuer), - project_id, - contribution.contributor, - contribution.id, - ), - Pallet::::contribution_unbond_for( - RuntimeOrigin::signed(contribution.contributor), - project_id, - contribution.contributor, - contribution.id, - ) - ) - } - - let post_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; - let post_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants); - - let all_participants_plmc_deltas = MockInstantiator::generic_map_operation( - vec![post_participants_plmc_balances, prev_participants_plmc_balances], - MergeOperation::Subtract, - ); - - let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - assert_eq!( - inst.get_project_details(project_id).cleanup, - Cleaner::Failure(CleanerState::Initialized(PhantomData)) - ); - assert_eq!(issuer_funding_delta, 0); - assert_eq!(all_participants_plmc_deltas, all_expected_payouts); - } - - // i.e consumer increase bug fixed with touch on pallet-assets - #[test] - fn no_limit_on_project_contributions_per_user() { - let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let project = |x| TestProjectParams { - expected_state: ProjectStatus::FundingSuccessful, - metadata: default_project_metadata(x, ISSUER), - evaluations: default_evaluations(), - bids: default_bids(), - community_contributions: default_community_buys(), - remainder_contributions: default_remainder_buys(), - issuer: ISSUER, - }; - let projects = (0..20).into_iter().map(|x| project(x)).collect_vec(); - async_features::create_multiple_projects_at(inst, projects); - } - - #[test] - fn evaluation_plmc_unbonded_after_funding_success() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let evaluations = default_evaluations(); - let evaluators = evaluations.accounts(); - - let project_id = inst.create_remainder_contributing_project( - default_project_metadata(inst.get_new_nonce(), ISSUER), - ISSUER, - evaluations.clone(), - default_bids(), - default_community_buys(), - ); - - let prev_reserved_plmc = - inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Evaluation(project_id).into()); - - let prev_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); - - inst.finish_funding(project_id).unwrap(); - inst.advance_time(::ManualAcceptanceDuration::get() + 1).unwrap(); - inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - inst.advance_time(10).unwrap(); - let post_unbond_amounts: Vec> = prev_reserved_plmc - .iter() - .map(|UserToPLMCBalance { account, .. }| UserToPLMCBalance::new(*account, Zero::zero())) - .collect(); - - inst.do_reserved_plmc_assertions(post_unbond_amounts.clone(), HoldReason::Evaluation(project_id).into()); - inst.do_reserved_plmc_assertions(post_unbond_amounts, HoldReason::Participation(project_id).into()); - - let post_free_plmc = inst.get_free_plmc_balances_for(evaluators); - - let increased_amounts = - MockInstantiator::generic_map_operation(vec![post_free_plmc, prev_free_plmc], MergeOperation::Subtract); - - assert_eq!(increased_amounts, MockInstantiator::calculate_evaluation_plmc_spent(evaluations)) - } - - #[test] - fn plmc_vesting_schedule_starts_automatically() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let mut bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids.clone(), - community_contributions.clone(), - remainder_contributions.clone(), - ); - - let price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); - bids = - stored_bids.into_iter().map(|bid| BidParams::new_with_defaults(bid.bidder, bid.final_ct_amount)).collect(); - let auction_locked_plmc = MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, price); - let community_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(community_contributions, price); - let remainder_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(remainder_contributions, price); - let all_plmc_locks = MockInstantiator::generic_map_operation( - vec![auction_locked_plmc, community_locked_plmc, remainder_locked_plmc], - MergeOperation::Add, - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - - inst.advance_time(10u64).unwrap(); - let details = inst.get_project_details(project_id); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - - for UserToPLMCBalance { account, plmc_amount } in all_plmc_locks { - let schedule = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &account, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(schedule.unwrap(), plmc_amount); - } - } - - #[test] - fn plmc_vesting_schedule_starts_manually() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids.clone(), - community_contributions.clone(), - remainder_contributions.clone(), - ); - - let price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - let details = inst.get_project_details(project_id); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); - - let stored_successful_bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) - .collect::>() - }); - let stored_contributions = - inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - - for bid in stored_successful_bids.clone() { - call_and_is_ok!( - inst, - Pallet::::do_start_bid_vesting_schedule_for(&bid.bidder, project_id, &bid.bidder, bid.id,) - ); - } - for contribution in stored_contributions { - call_and_is_ok!( - inst, - Pallet::::do_start_contribution_vesting_schedule_for( - &contribution.contributor, - project_id, - &contribution.contributor, - contribution.id, - ) - ); - } - - let auction_locked_plmc = MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, price); - let community_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(community_contributions, price); - let remainder_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(remainder_contributions, price); - let all_plmc_locks = MockInstantiator::generic_map_operation( - vec![auction_locked_plmc, community_locked_plmc, remainder_locked_plmc], - MergeOperation::Add, - ); - - for UserToPLMCBalance { account, plmc_amount } in all_plmc_locks { - let schedule = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &account, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(schedule.unwrap(), plmc_amount); - } - } - - #[test] - fn plmc_vesting_full_amount() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids, - community_contributions, - remainder_contributions, - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - - inst.advance_time(10u64).unwrap(); - let details = inst.get_project_details(project_id); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - - let stored_successful_bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) - .collect::>() - }); - - let stored_contributions = - inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - - let total_bid_plmc_in_vesting: Vec> = stored_successful_bids - .iter() - .map(|bid| (bid.bidder, bid.plmc_vesting_info.unwrap().total_amount).into()) - .collect_vec(); - - let total_contribution_plmc_in_vesting: Vec> = stored_contributions - .iter() - .map(|contribution| (contribution.contributor, contribution.plmc_vesting_info.unwrap().total_amount).into()) - .collect_vec(); - - let total_participant_plmc_in_vesting = MockInstantiator::generic_map_operation( - vec![total_bid_plmc_in_vesting, total_contribution_plmc_in_vesting], - MergeOperation::Add, - ); - - inst.advance_time((10 * DAYS).into()).unwrap(); - - for UserToPLMCBalance { account, plmc_amount } in total_participant_plmc_in_vesting { - let prev_free_balance = inst.execute(|| ::NativeCurrency::balance(&account)); - - inst.execute(|| Pallet::::do_vest_plmc_for(account, project_id, account)).unwrap(); - - let post_free_balance = inst.execute(|| ::NativeCurrency::balance(&account)); - assert_eq!(plmc_amount, post_free_balance - prev_free_balance); - } - } - - #[test] - fn plmc_vesting_partial_amount() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER; - let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_contributions = default_community_buys(); - let remainder_contributions = default_remainder_buys(); - - let project_id = inst.create_finished_project( - project_metadata, - issuer, - evaluations, - bids, - community_contributions, - remainder_contributions, - ); - - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.advance_time(15u64).unwrap(); - let details = inst.get_project_details(project_id); - assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - let vest_start_block = details.funding_end_block.unwrap(); - let stored_successful_bids = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) - .collect::>() - }); - let stored_contributions = - inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - - let bidder_vesting = - stored_successful_bids.iter().map(|bid| (bid.bidder, bid.plmc_vesting_info.unwrap())).collect_vec(); - let contributor_vesting = stored_contributions - .iter() - .map(|contribution| (contribution.contributor, contribution.plmc_vesting_info.unwrap())) - .collect_vec(); - - let participant_vesting_infos: Vec<(AccountIdOf, Vec>)> = - MockInstantiator::generic_map_merge_reduce( - vec![bidder_vesting, contributor_vesting], - |map| map.0, - Vec::new(), - |map, mut vestings| { - vestings.push(map.1); - vestings - }, - ); - - let now = inst.current_block(); - for (participant, vesting_infos) in participant_vesting_infos { - let vested_amount = vesting_infos.into_iter().fold(0u128, |acc, vesting_info| { - acc + vesting_info.amount_per_block * min(vesting_info.duration, now - vest_start_block) as u128 - }); - - let prev_free_balance = inst.execute(|| ::NativeCurrency::balance(&participant)); - - inst.execute(|| Pallet::::do_vest_plmc_for(participant, project_id, participant)).unwrap(); - - let post_free_balance = inst.execute(|| ::NativeCurrency::balance(&participant)); - assert_eq!(vested_amount, post_free_balance - prev_free_balance); - } - } - - #[test] - fn ct_treasury_mints() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let treasury_account = ::ContributionTreasury::get(); - - let project_metadata = ProjectMetadataOf:: { - token_information: default_token_information(), - mainnet_token_max_supply: 8_000_000 * ASSET_UNIT, - total_allocation_size: 1_000_000 * ASSET_UNIT, - auction_round_allocation_percentage: Percent::from_percent(50u8), - minimum_price: PriceOf::::from_float(10.0), - bidding_ticket_sizes: BiddingTicketSizes { - professional: TicketSize::new(Some(5000 * US_DOLLAR), None), - institutional: TicketSize::new(Some(5000 * US_DOLLAR), None), - phantom: Default::default(), - }, - contributing_ticket_sizes: ContributingTicketSizes { - retail: TicketSize::new(None, None), - professional: TicketSize::new(None, None), - institutional: TicketSize::new(None, None), - phantom: Default::default(), - }, - participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), - funding_destination_account: ISSUER, - offchain_information_hash: Some(hashed(METADATA)), - }; - let mut counter: u8 = 0u8; - let mut with_different_metadata = |mut project: ProjectMetadataOf| { - let mut binding = project.offchain_information_hash.unwrap(); - let h256_bytes = binding.as_fixed_bytes_mut(); - h256_bytes[0] = counter; - counter += 1u8; - project.offchain_information_hash = Some(binding); - project - }; - - let price = project_metadata.minimum_price; - - // Failed project has no mints on the treasury - let project_20_percent = inst.create_finished_project( - with_different_metadata(project_metadata.clone()), - ISSUER, - default_evaluations(), - default_bids_from_ct_percent(10), - default_community_contributions_from_ct_percent(10), - vec![], - ); - inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); - inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); - let ct_balance = inst.execute(|| { - ::ContributionTokenCurrency::balance(project_20_percent, treasury_account) - }); - assert_eq!(ct_balance, 0); - - // 50% funded project can have mints on the treasury if issuer accepts or enough time passes for automatic acceptance - let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; - let fee_8_percent = Percent::from_percent(8) * 4_000_000 * US_DOLLAR; - let fee_6_percent = Percent::from_percent(6) * 0 * US_DOLLAR; - let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; - let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); - - let project_50_percent = inst.create_finished_project( - with_different_metadata(project_metadata.clone()), - ISSUER, - default_evaluations(), - default_bids_from_ct_percent(25), - default_community_contributions_from_ct_percent(20), - default_remainder_contributions_from_ct_percent(5), - ); - inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); - inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); - let ct_balance = inst.execute(|| { - ::ContributionTokenCurrency::balance(project_50_percent, treasury_account) - }); - let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; - let expected_long_term_holder_bonus_minted = Percent::from_percent(50) * total_ct_fee; - assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); - - // 80% funded project can have mints on the treasury if issuer accepts or enough time passes for automatic acceptance - let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; - let fee_8_percent = Percent::from_percent(8) * 5_000_000 * US_DOLLAR; - let fee_6_percent = Percent::from_percent(6) * 2_000_000 * US_DOLLAR; - let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; - let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); - - let project_80_percent = inst.create_finished_project( - with_different_metadata(project_metadata.clone()), - ISSUER, - default_evaluations(), - default_bids_from_ct_percent(40), - default_community_contributions_from_ct_percent(30), - default_remainder_contributions_from_ct_percent(10), - ); - inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); - inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); - let ct_balance = inst.execute(|| { - ::ContributionTokenCurrency::balance(project_80_percent, treasury_account) - }); - let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; - let expected_long_term_holder_bonus_minted = Percent::from_percent(50) * total_ct_fee; - assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); - - // 98% funded project always has mints on the treasury - let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; - let fee_8_percent = Percent::from_percent(8) * 5_000_000 * US_DOLLAR; - let fee_6_percent = Percent::from_percent(6) * 3_800_000 * US_DOLLAR; - let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; - let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); - - let project_98_percent = inst.create_finished_project( - with_different_metadata(project_metadata.clone()), - ISSUER, - default_evaluations(), - default_bids_from_ct_percent(49), - default_community_contributions_from_ct_percent(39), - default_remainder_contributions_from_ct_percent(10), - ); - inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); - let ct_balance = inst.execute(|| { - ::ContributionTokenCurrency::balance(project_98_percent, treasury_account) - }); - let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; - let lthb_percent = Perquintill::from_percent(20) + Perquintill::from_percent(30) * Perquintill::from_percent(2); - let expected_long_term_holder_bonus_minted = lthb_percent * total_ct_fee; - assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); - - // Test the touch on the treasury ct account by the issuer. - // We create more CT accounts that the account can use provider references for, - // so if it succeeds, then it means the touch was successful. - let consumer_limit: u32 = ::MaxConsumers::get(); - - // we want to test ct mints on treasury of 1 over the consumer limit, - // and we already minted 3 contribution tokens on previous tests. - for i in 0..consumer_limit + 1u32 - 3u32 { - let _project_98_percent = inst.create_finished_project( - with_different_metadata(project_metadata.clone()), - ISSUER + i + 1000, - default_evaluations(), - default_bids_from_ct_percent(49), - default_community_contributions_from_ct_percent(39), - default_remainder_contributions_from_ct_percent(10), - ); - inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); - } - } -} +// mod funding_end { +// use super::*; + +// #[test] +// fn automatic_fail_less_eq_33_percent() { +// for funding_percent in (1..=33).step_by(5) { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); +// let min_price = project_metadata.minimum_price; +// let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * +// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// vec![100u8], +// vec![BIDDER_1], +// vec![10u8], +// ); +// let contributions = MockInstantiator::generate_contributions_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let project_id = +// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// } +// } + +// #[test] +// fn automatic_success_bigger_eq_90_percent() { +// for funding_percent in (90..=100).step_by(2) { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); +// let min_price = project_metadata.minimum_price; +// let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * +// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let contributions = MockInstantiator::generate_contributions_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let project_id = +// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// } +// } + +// #[test] +// fn manual_outcome_above33_to_below90() { +// for funding_percent in (34..90).step_by(5) { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); +// let min_price = project_metadata.minimum_price; +// let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * +// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let contributions = MockInstantiator::generate_contributions_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let project_id = +// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); +// } +// } + +// #[test] +// fn manual_acceptance() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); +// let min_price = project_metadata.minimum_price; +// let twenty_percent_funding_usd = Perquintill::from_percent(55) * +// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let contributions = MockInstantiator::generate_contributions_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let project_id = +// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + +// let project_id = project_id; +// inst.execute(|| { +// PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::AcceptFunding) +// }) +// .unwrap(); + +// inst.advance_time(1u64).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + +// assert_matches!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Initialized(_))); +// inst.test_ct_created_for(project_id); + +// inst.advance_time(10u64).unwrap(); +// assert_matches!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Success(CleanerState::Finished(PhantomData)) +// ); +// } + +// #[test] +// fn manual_rejection() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); +// let min_price = project_metadata.minimum_price; +// let twenty_percent_funding_usd = Perquintill::from_percent(55) * +// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let contributions = MockInstantiator::generate_contributions_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let project_id = +// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + +// let project_id = project_id; +// inst.execute(|| { +// PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::RejectFunding) +// }) +// .unwrap(); + +// inst.advance_time(1u64).unwrap(); + +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// assert_matches!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Failure(CleanerState::Initialized(PhantomData)) +// ); + +// inst.test_ct_not_created_for(project_id); + +// inst.advance_time(10u64).unwrap(); +// assert_matches!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Failure(CleanerState::Finished(PhantomData)) +// ); +// } + +// #[test] +// fn automatic_acceptance_on_manual_decision_after_time_delta() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); +// let min_price = project_metadata.minimum_price; +// let twenty_percent_funding_usd = Perquintill::from_percent(55) * +// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let contributions = MockInstantiator::generate_contributions_from_total_usd( +// Percent::from_percent(50u8) * twenty_percent_funding_usd, +// min_price, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let project_id = +// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + +// let project_id = project_id; +// inst.advance_time(1u64 + ::ManualAcceptanceDuration::get()).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + +// inst.test_ct_created_for(project_id); + +// inst.settle_project(project_id).unwrap(); +// } + +// #[test] +// fn evaluators_get_slashed_funding_accepted() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = project_from_funding_reached(&mut inst, 43u64); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + +// let old_evaluation_locked_plmc = inst +// .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) +// .into_iter() +// .filter(|item| item.plmc_amount > Zero::zero()) +// .collect::>>(); + +// let evaluators = old_evaluation_locked_plmc.accounts(); + +// let old_participation_locked_plmc = +// inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); +// let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); + +// call_and_is_ok!( +// inst, +// PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::AcceptFunding) +// ); +// inst.advance_time(1u64).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); +// assert_matches!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Success(CleanerState::Finished(PhantomData)) +// ); + +// let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); +// let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( +// vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], +// MergeOperation::Add, +// ); + +// let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); + +// assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); +// } + +// #[test] +// fn evaluators_get_slashed_funding_funding_rejected() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = project_from_funding_reached(&mut inst, 56u64); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + +// let old_evaluation_locked_plmc = inst +// .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) +// .into_iter() +// .filter(|item| item.plmc_amount > Zero::zero()) +// .collect::>>(); + +// let evaluators = old_evaluation_locked_plmc.accounts(); + +// let old_participation_locked_plmc = +// inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); +// let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); + +// call_and_is_ok!( +// inst, +// PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::RejectFunding) +// ); +// inst.advance_time(1u64).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); +// assert_matches!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Failure(CleanerState::Finished(PhantomData)) +// ); + +// let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); +// let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( +// vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], +// MergeOperation::Add, +// ); +// let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); + +// assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); +// } + +// #[test] +// fn evaluators_get_slashed_funding_failed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = project_from_funding_reached(&mut inst, 24u64); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); + +// let old_evaluation_locked_plmc = inst +// .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) +// .into_iter() +// .filter(|item| item.plmc_amount > Zero::zero()) +// .collect::>(); + +// let evaluators = old_evaluation_locked_plmc.accounts(); + +// let old_participation_locked_plmc = +// inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); +// let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); + +// inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); +// assert_matches!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Failure(CleanerState::Finished(PhantomData)) +// ); + +// let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); +// let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( +// vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], +// MergeOperation::Add, +// ); + +// let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); + +// assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); +// } + +// #[test] +// fn ct_minted_automatically() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations.clone(), +// bids.clone(), +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.status, ProjectStatus::FundingSuccessful); + +// let reward_info = match details.evaluation_round_info.evaluators_outcome { +// EvaluatorsOutcome::Rewarded(reward_info) => Some(reward_info), +// _ => None, +// }; + +// let evaluators = evaluations.accounts(); +// let evaluator_ct_amounts = evaluators +// .iter() +// .map(|account| { +// let evaluations = inst.execute(|| { +// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// let total_evaluator_ct_rewarded = match reward_info.clone() { +// Some(info) => { +// evaluations +// .iter() +// .map(|evaluation| { +// Pallet::::calculate_evaluator_reward(evaluation, &info) +// }) +// .sum::() +// }, +// None => Zero::zero() +// }; + +// (account, total_evaluator_ct_rewarded) +// }) +// .collect_vec(); + +// let bidders = bids.accounts(); +// let bidder_ct_amounts = bidders +// .iter() +// .map(|account| { +// let bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); + +// (account, total_bidder_ct_rewarded) +// }) +// .collect_vec(); + +// let community_accounts = community_contributions.accounts(); +// let remainder_accounts = remainder_contributions.accounts(); +// let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); +// let contributor_ct_amounts = all_contributors +// .map(|account| { +// let contributions = inst.execute(|| { +// Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// let total_contributor_ct_rewarded = +// contributions.iter().map(|contribution| contribution.ct_amount).sum::(); + +// (account, total_contributor_ct_rewarded) +// }) +// .collect_vec(); + +// let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( +// vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], +// |item| item.0, +// Zero::zero(), +// |item, accumulator| accumulator + item.1, +// ); + +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + +// inst.settle_project(project_id).unwrap(); + +// for (account, amount) in all_ct_expectations { +// let minted = +// inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); +// assert_eq!(minted, amount); +// } +// } + +// #[test] +// fn ct_minted_manually() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations.clone(), +// bids.clone(), +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.status, ProjectStatus::FundingSuccessful); +// assert_eq!(details.cleanup, Cleaner::NotReady); +// // do_end_funding +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// assert_eq!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Success(CleanerState::Initialized(PhantomData)) +// ); + +// let evaluators = evaluations.accounts(); +// let evaluator_ct_amounts = evaluators +// .iter() +// .map(|account| { +// let evaluations = inst.execute(|| { +// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for evaluation in evaluations.iter() { +// inst.execute(|| { +// assert_ok!(Pallet::::evaluation_reward_payout_for( +// RuntimeOrigin::signed(evaluation.evaluator), +// project_id, +// evaluation.evaluator, +// evaluation.id, +// )); +// }); +// } +// let evaluations = inst.execute(|| { +// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// let total_evaluator_ct_rewarded = evaluations +// .iter() +// .map(|evaluation| evaluation.rewarded_or_slashed) +// .map(|reward_or_slash| { +// if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { +// balance +// } else { +// Zero::zero() +// } +// }) +// .sum::(); + +// (account, total_evaluator_ct_rewarded) +// }) +// .collect_vec(); + +// let bidders = bids.accounts(); +// let bidder_ct_amounts = bidders +// .iter() +// .map(|account| { +// let bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for bid in bids.iter() { +// inst.execute(|| { +// assert_ok!(Pallet::::bid_ct_mint_for( +// RuntimeOrigin::signed(bid.bidder), +// project_id, +// bid.bidder, +// bid.id, +// )); +// }); +// } + +// let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); + +// (account, total_bidder_ct_rewarded) +// }) +// .collect_vec(); + +// let community_accounts = community_contributions.accounts(); +// let remainder_accounts = remainder_contributions.accounts(); +// let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); +// let contributor_ct_amounts = all_contributors +// .map(|account| { +// let contributions = inst.execute(|| { +// Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for contribution in contributions.iter() { +// inst.execute(|| { +// assert_ok!(Pallet::::contribution_ct_mint_for( +// RuntimeOrigin::signed(contribution.contributor), +// project_id, +// contribution.contributor, +// contribution.id, +// )); +// }); +// } + +// let total_contributor_ct_rewarded = +// contributions.iter().map(|contribution| contribution.ct_amount).sum::(); + +// (account, total_contributor_ct_rewarded) +// }) +// .collect_vec(); + +// let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( +// vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], +// |item| item.0, +// Zero::zero(), +// |item, accumulator| accumulator + item.1, +// ); + +// for (account, amount) in all_ct_expectations { +// let minted = +// inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); +// assert_eq!(minted, amount, "Account: {}", account); +// } + +// let details = inst.get_project_details(project_id); +// assert_eq!(details.status, ProjectStatus::FundingSuccessful); +// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); +// } + +// #[test] +// fn cannot_mint_ct_twice_manually() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations.clone(), +// bids.clone(), +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.status, ProjectStatus::FundingSuccessful); +// assert_eq!(details.cleanup, Cleaner::NotReady); +// // do_end_funding +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// assert_eq!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Success(CleanerState::Initialized(PhantomData)) +// ); + +// let evaluators = evaluations.accounts(); +// let evaluator_ct_amounts = evaluators +// .iter() +// .map(|account| { +// let evaluations = inst.execute(|| { +// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for evaluation in evaluations.iter() { +// inst.execute(|| { +// assert_ok!(Pallet::::evaluation_reward_payout_for( +// RuntimeOrigin::signed(evaluation.evaluator), +// project_id, +// evaluation.evaluator, +// evaluation.id, +// )); +// assert_noop!( +// Pallet::::evaluation_reward_payout_for( +// RuntimeOrigin::signed(evaluation.evaluator), +// project_id, +// evaluation.evaluator, +// evaluation.id, +// ), +// Error::::NotAllowed +// ); +// }); +// } +// let evaluations = inst.execute(|| { +// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// let total_evaluator_ct_rewarded = evaluations +// .iter() +// .map(|evaluation| evaluation.rewarded_or_slashed) +// .map(|reward_or_slash| { +// if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { +// balance +// } else { +// Zero::zero() +// } +// }) +// .sum::(); + +// (account, total_evaluator_ct_rewarded) +// }) +// .collect_vec(); + +// let bidders = bids.accounts(); +// let bidder_ct_amounts = bidders +// .iter() +// .map(|account| { +// let bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for bid in bids.iter() { +// inst.execute(|| { +// assert_ok!(Pallet::::bid_ct_mint_for( +// RuntimeOrigin::signed(bid.bidder), +// project_id, +// bid.bidder, +// bid.id, +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::bid_ct_mint_for( +// RuntimeOrigin::signed(bid.bidder), +// project_id, +// bid.bidder, +// bid.id, +// ), +// Error::::NotAllowed +// ); +// }); +// } + +// let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); + +// (account, total_bidder_ct_rewarded) +// }) +// .collect_vec(); + +// let community_accounts = community_contributions.accounts(); +// let remainder_accounts = remainder_contributions.accounts(); +// let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); +// let contributor_ct_amounts = all_contributors +// .map(|account| { +// let contributions = inst.execute(|| { +// Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for contribution in contributions.iter() { +// inst.execute(|| { +// assert_ok!(Pallet::::contribution_ct_mint_for( +// RuntimeOrigin::signed(contribution.contributor), +// project_id, +// contribution.contributor, +// contribution.id, +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribution_ct_mint_for( +// RuntimeOrigin::signed(contribution.contributor), +// project_id, +// contribution.contributor, +// contribution.id, +// ), +// Error::::NotAllowed +// ); +// }); +// } + +// let total_contributor_ct_rewarded = +// contributions.iter().map(|contribution| contribution.ct_amount).sum::(); + +// (account, total_contributor_ct_rewarded) +// }) +// .collect_vec(); + +// let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( +// vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], +// |item| item.0, +// Zero::zero(), +// |item, accumulator| accumulator + item.1, +// ); + +// for (account, amount) in all_ct_expectations { +// let minted = +// inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); +// assert_eq!(minted, amount, "Account: {}", account); +// } + +// let details = inst.get_project_details(project_id); +// assert_eq!(details.status, ProjectStatus::FundingSuccessful); +// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); +// } + +// #[test] +// fn cannot_mint_ct_manually_after_automatic_mint() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations.clone(), +// bids.clone(), +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.status, ProjectStatus::FundingSuccessful); +// assert_eq!(details.cleanup, Cleaner::NotReady); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// assert_eq!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Success(CleanerState::Initialized(PhantomData)) +// ); +// inst.advance_time(1).unwrap(); +// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); + +// let evaluators = evaluations.accounts(); +// let evaluator_ct_amounts = evaluators +// .iter() +// .map(|account| { +// let evaluations = inst.execute(|| { +// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for evaluation in evaluations.iter() { +// inst.execute(|| { +// assert_noop!( +// Pallet::::evaluation_reward_payout_for( +// RuntimeOrigin::signed(evaluation.evaluator), +// project_id, +// evaluation.evaluator, +// evaluation.id, +// ), +// Error::::NotAllowed +// ); +// }); +// } +// let evaluations = inst.execute(|| { +// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// let total_evaluator_ct_rewarded = evaluations +// .iter() +// .map(|evaluation| evaluation.rewarded_or_slashed) +// .map(|reward_or_slash| { +// if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { +// balance +// } else { +// Zero::zero() +// } +// }) +// .sum::(); + +// (account, total_evaluator_ct_rewarded) +// }) +// .collect_vec(); + +// let bidders = bids.accounts(); +// let bidder_ct_amounts = bidders +// .iter() +// .map(|account| { +// let bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for bid in bids.iter() { +// inst.execute(|| { +// assert_noop!( +// Pallet::::bid_ct_mint_for( +// RuntimeOrigin::signed(bid.bidder), +// project_id, +// bid.bidder, +// bid.id, +// ), +// Error::::NotAllowed +// ); +// }); +// } + +// let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); + +// (account, total_bidder_ct_rewarded) +// }) +// .collect_vec(); + +// let community_accounts = community_contributions.accounts(); +// let remainder_accounts = remainder_contributions.accounts(); +// let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); +// let contributor_ct_amounts = all_contributors +// .map(|account| { +// let contributions = inst.execute(|| { +// Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() +// }); +// for contribution in contributions.iter() { +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribution_ct_mint_for( +// RuntimeOrigin::signed(contribution.contributor), +// project_id, +// contribution.contributor, +// contribution.id, +// ), +// Error::::NotAllowed +// ); +// }); +// } + +// let total_contributor_ct_rewarded = +// contributions.iter().map(|contribution| contribution.ct_amount).sum::(); + +// (account, total_contributor_ct_rewarded) +// }) +// .collect_vec(); + +// let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( +// vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], +// |item| item.0, +// Zero::zero(), +// |item, accumulator| accumulator + item.1, +// ); + +// for (account, amount) in all_ct_expectations { +// let minted = +// inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); +// assert_eq!(minted, amount, "Account: {}", account); +// } +// } + +// #[test] +// fn multiplier_gets_correct_vesting_duration() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = vec![ +// BidParams::new(BIDDER_1, 325_000 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_2, 75_000 * ASSET_UNIT, 2u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_3, 50_000 * ASSET_UNIT, 3u8, AcceptedFundingAsset::USDT), +// ]; +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids, +// community_contributions, +// remainder_contributions, +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + +// inst.advance_time(10u64).unwrap(); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); + +// let mut stored_bids = +// inst.execute(|| Bids::::iter_prefix_values((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, +// FixedU128::from_rational(2167, 1000).saturating_mul_int(one_week_in_blocks as u64) +// ); +// assert_eq!( +// stored_bids[2].plmc_vesting_info.unwrap().duration, +// FixedU128::from_rational(4334, 1000).saturating_mul_int(one_week_in_blocks as u64) +// ); +// } + +// #[test] +// fn funding_assets_are_paid_manually_to_issuer() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids, +// community_contributions, +// remainder_contributions, +// ); + +// let final_winning_bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) +// .collect::>() +// }); +// let final_bid_payouts = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) +// .map(|bid| { +// UserToForeignAssets::new( +// bid.bidder, +// bid.funding_asset_amount_locked, +// bid.funding_asset.to_assethub_id(), +// ) +// }) +// .collect::>>() +// }); +// let final_contributions = +// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); +// let final_contribution_payouts = inst.execute(|| { +// Contributions::::iter_prefix_values((project_id,)) +// .map(|contribution| { +// UserToForeignAssets::new( +// contribution.contributor, +// contribution.funding_asset_amount, +// contribution.funding_asset.to_assethub_id(), +// ) +// }) +// .collect::>>() +// }); + +// let total_expected_bid_payout = +// final_bid_payouts.iter().map(|bid| bid.asset_amount).sum::>(); +// let total_expected_contribution_payout = final_contribution_payouts +// .iter() +// .map(|contribution| contribution.asset_amount) +// .sum::>(); + +// let prev_issuer_funding_balance = +// inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; + +// let prev_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( +// final_bid_payouts[0].asset_id, +// vec![Pallet::::fund_account_id(project_id)], +// )[0] +// .asset_amount; + +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// assert_eq!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Success(CleanerState::Initialized(PhantomData)) +// ); +// for bid in final_winning_bids { +// inst.execute(|| { +// Pallet::::payout_bid_funds_for( +// RuntimeOrigin::signed(issuer), +// project_id, +// bid.bidder, +// bid.id, +// ) +// }) +// .unwrap(); +// } +// for contribution in final_contributions { +// inst.execute(|| { +// Pallet::::payout_contribution_funds_for( +// RuntimeOrigin::signed(issuer), +// project_id, +// contribution.contributor, +// contribution.id, +// ) +// }) +// .unwrap(); +// } +// let post_issuer_funding_balance = +// inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; + +// let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( +// final_bid_payouts[0].asset_id, +// vec![Pallet::::fund_account_id(project_id)], +// )[0] +// .asset_amount; +// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; +// let project_pot_funding_delta = prev_project_pot_funding_balance - post_project_pot_funding_balance; + +// assert_eq!(issuer_funding_delta - total_expected_bid_payout, total_expected_contribution_payout); +// assert_eq!(issuer_funding_delta, project_pot_funding_delta); + +// assert_eq!(post_project_pot_funding_balance, 0u128); +// } + +// #[test] +// fn funding_assets_are_paid_automatically_to_issuer() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids, +// community_contributions, +// remainder_contributions, +// ); + +// let final_bid_payouts = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) +// .map(|bid| { +// UserToForeignAssets::new( +// bid.bidder, +// bid.funding_asset_amount_locked, +// bid.funding_asset.to_assethub_id(), +// ) +// }) +// .collect::>>() +// }); +// let final_contribution_payouts = inst.execute(|| { +// Contributions::::iter_prefix_values((project_id,)) +// .map(|contribution| { +// UserToForeignAssets::new( +// contribution.contributor, +// contribution.funding_asset_amount, +// contribution.funding_asset.to_assethub_id(), +// ) +// }) +// .collect::>>() +// }); + +// let total_expected_bid_payout = +// final_bid_payouts.iter().map(|bid| bid.asset_amount).sum::>(); +// let total_expected_contribution_payout = final_contribution_payouts +// .iter() +// .map(|contribution| contribution.asset_amount) +// .sum::>(); + +// let prev_issuer_funding_balance = +// inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; + +// let prev_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( +// final_bid_payouts[0].asset_id, +// vec![Pallet::::fund_account_id(project_id)], +// )[0] +// .asset_amount; + +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// assert_eq!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Success(CleanerState::Initialized(PhantomData)) +// ); +// inst.advance_time(1u64).unwrap(); +// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); + +// let post_issuer_funding_balance = +// inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; + +// let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( +// final_bid_payouts[0].asset_id, +// vec![Pallet::::fund_account_id(project_id)], +// )[0] +// .asset_amount; +// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; +// let project_pot_funding_delta = prev_project_pot_funding_balance - post_project_pot_funding_balance; + +// assert_eq!(issuer_funding_delta - total_expected_bid_payout, total_expected_contribution_payout); +// assert_eq!(issuer_funding_delta, project_pot_funding_delta); + +// assert_eq!(post_project_pot_funding_balance, 0u128); +// } + +// #[test] +// fn funding_assets_are_released_automatically_on_funding_fail() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); + +// let auction_allocation = +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int(auction_allocation), +// project_metadata.minimum_price, +// default_weights(), +// default_bidders(), +// default_bidder_multipliers(), +// ); +// let community_contributions = MockInstantiator::generate_contributions_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ); +// let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_remainder_contributors(), +// default_remainder_contributor_multipliers(), +// ); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids, +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); + +// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let expected_bid_payouts = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .map(|bid| { +// UserToForeignAssets::::new( +// bid.bidder, +// bid.funding_asset_amount_locked, +// bid.funding_asset.to_assethub_id(), +// ) +// }) +// .sorted_by_key(|bid| bid.account) +// .collect::>>() +// }); +// let expected_community_contribution_payouts = +// MockInstantiator::calculate_contributed_funding_asset_spent(community_contributions, final_price); +// let expected_remainder_contribution_payouts = +// MockInstantiator::calculate_contributed_funding_asset_spent(remainder_contributions, final_price); +// let all_expected_payouts = MockInstantiator::generic_map_operation( +// vec![ +// expected_bid_payouts.clone(), +// expected_community_contribution_payouts, +// expected_remainder_contribution_payouts, +// ], +// MergeOperation::Add, +// ); + +// let prev_issuer_funding_balance = +// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; +// let all_participants = all_expected_payouts.accounts(); +// let prev_participants_funding_balances = +// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants.clone()); + +// call_and_is_ok!( +// inst, +// Pallet::::decide_project_outcome( +// RuntimeOrigin::signed(issuer), +// project_id, +// FundingOutcomeDecision::RejectFunding +// ) +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.advance_time(10).unwrap(); +// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Failure(CleanerState::Finished(PhantomData))); + +// let post_issuer_funding_balance = +// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; +// let post_participants_funding_balances = +// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants); +// let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( +// expected_bid_payouts[0].asset_id, +// vec![Pallet::::fund_account_id(project_id)], +// )[0] +// .asset_amount; + +// let all_participants_funding_delta = MockInstantiator::generic_map_operation( +// vec![prev_participants_funding_balances, post_participants_funding_balances], +// MergeOperation::Add, +// ); + +// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; + +// assert_eq!(issuer_funding_delta, 0); +// assert_eq!(post_project_pot_funding_balance, 0u128); +// assert_eq!(all_expected_payouts, all_participants_funding_delta); +// } + +// #[test] +// fn funding_assets_are_released_manually_on_funding_fail() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let auction_allocation = +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int(auction_allocation), +// project_metadata.minimum_price, +// default_weights(), +// default_bidders(), +// default_bidder_multipliers(), +// ); +// let community_contributions = MockInstantiator::generate_contributions_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ); +// let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_remainder_contributors(), +// default_remainder_contributor_multipliers(), +// ); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids, +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); +// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let expected_bid_payouts = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .map(|bid| { +// UserToForeignAssets::::new( +// bid.bidder, +// bid.funding_asset_amount_locked, +// bid.funding_asset.to_assethub_id(), +// ) +// }) +// .sorted_by_key(|item| item.account) +// .collect::>>() +// }); +// let expected_community_contribution_payouts = +// MockInstantiator::calculate_contributed_funding_asset_spent(community_contributions, final_price); +// let expected_remainder_contribution_payouts = +// MockInstantiator::calculate_contributed_funding_asset_spent(remainder_contributions, final_price); +// let all_expected_payouts = MockInstantiator::generic_map_operation( +// vec![ +// expected_bid_payouts.clone(), +// expected_community_contribution_payouts, +// expected_remainder_contribution_payouts, +// ], +// MergeOperation::Add, +// ); + +// let prev_issuer_funding_balance = +// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; +// let all_participants = all_expected_payouts.accounts(); +// let prev_participants_funding_balances = +// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants.clone()); + +// call_and_is_ok!( +// inst, +// Pallet::::decide_project_outcome( +// RuntimeOrigin::signed(issuer), +// project_id, +// FundingOutcomeDecision::RejectFunding +// ) +// ); + +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + +// let stored_bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) +// .collect::>() +// }); +// let stored_contributions = +// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); + +// for bid in stored_bids { +// call_and_is_ok!( +// inst, +// Pallet::::release_bid_funds_for( +// RuntimeOrigin::signed(issuer), +// project_id, +// bid.bidder, +// bid.id, +// ) +// ) +// } + +// for contribution in stored_contributions { +// call_and_is_ok!( +// inst, +// Pallet::::release_contribution_funds_for( +// RuntimeOrigin::signed(issuer), +// project_id, +// contribution.contributor, +// contribution.id, +// ) +// ) +// } + +// let post_issuer_funding_balance = +// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; +// let post_participants_funding_balances = +// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants); +// let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( +// expected_bid_payouts[0].asset_id, +// vec![Pallet::::fund_account_id(project_id)], +// )[0] +// .asset_amount; + +// let all_participants_funding_delta = MockInstantiator::generic_map_operation( +// vec![prev_participants_funding_balances, post_participants_funding_balances], +// MergeOperation::Add, +// ); + +// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; + +// assert_eq!(issuer_funding_delta, 0); +// assert_eq!(post_project_pot_funding_balance, 0u128); +// assert_eq!(all_expected_payouts, all_participants_funding_delta); +// } + +// #[test] +// fn plmc_bonded_is_returned_automatically_on_funding_fail() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let auction_allocation = +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int(auction_allocation), +// project_metadata.minimum_price, +// default_weights(), +// default_bidders(), +// default_bidder_multipliers(), +// ); +// let community_contributions = MockInstantiator::generate_contributions_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ); +// let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_remainder_contributors(), +// default_remainder_contributor_multipliers(), +// ); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations.clone(), +// bids.clone(), +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); +// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); + +// let expected_evaluators_and_contributors_payouts = +// MockInstantiator::calculate_total_plmc_locked_from_evaluations_and_remainder_contributions( +// evaluations.clone(), +// remainder_contributions.clone(), +// final_price, +// true, +// ); +// let expected_bid_payouts = +// MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, final_price); +// let expected_community_contribution_payouts = +// MockInstantiator::calculate_contributed_plmc_spent(community_contributions.clone(), final_price); +// let all_expected_payouts = MockInstantiator::generic_map_operation( +// vec![ +// expected_evaluators_and_contributors_payouts.clone(), +// expected_bid_payouts, +// expected_community_contribution_payouts, +// ], +// MergeOperation::Add, +// ); +// println!("all expected payouts {:?}", all_expected_payouts); +// for payout in all_expected_payouts.clone() { +// let evaluation_hold = inst.execute(|| { +// <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( +// &HoldReason::Evaluation(project_id).into(), +// &payout.account, +// ) +// }); +// let participation_hold = inst.execute(|| { +// <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( +// &HoldReason::Participation(project_id).into(), +// &payout.account, +// ) +// }); +// println!("account {:?} has evaluation hold {:?}", payout.account, evaluation_hold); +// println!("account {:?} has participation hold {:?}", payout.account, participation_hold); +// } +// let deposit_required = <::ContributionTokenCurrency as AccountTouch< +// ProjectId, +// AccountIdOf, +// >>::deposit_required(project_id); +// let all_expected_payouts = all_expected_payouts +// .into_iter() +// .map(|UserToPLMCBalance { account, plmc_amount }| { +// UserToPLMCBalance::new(account, plmc_amount + deposit_required) +// }) +// .collect::>(); + +// let prev_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; + +// let all_participants = all_expected_payouts.accounts(); +// let prev_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants.clone()); + +// call_and_is_ok!( +// inst, +// Pallet::::decide_project_outcome( +// RuntimeOrigin::signed(issuer), +// project_id, +// FundingOutcomeDecision::RejectFunding +// ) +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.advance_time(10).unwrap(); +// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Failure(CleanerState::Finished(PhantomData))); + +// let post_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; +// let post_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants); + +// let all_participants_plmc_deltas = MockInstantiator::generic_map_operation( +// vec![post_participants_plmc_balances, prev_participants_plmc_balances], +// MergeOperation::Subtract, +// ); + +// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; + +// assert_eq!(issuer_funding_delta, 0); +// assert_eq!(all_participants_plmc_deltas, all_expected_payouts); +// } + +// #[test] +// fn plmc_bonded_is_returned_manually_on_funding_fail() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let auction_allocation = +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; +// let evaluations = default_evaluations(); +// let bids = MockInstantiator::generate_bids_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int(auction_allocation), +// project_metadata.minimum_price, +// default_weights(), +// default_bidders(), +// default_bidder_multipliers(), +// ); +// let community_contributions = MockInstantiator::generate_contributions_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ); +// let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_remainder_contributors(), +// default_remainder_contributor_multipliers(), +// ); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations.clone(), +// bids.clone(), +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); +// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); + +// let expected_evaluators_and_contributors_payouts = +// MockInstantiator::calculate_total_plmc_locked_from_evaluations_and_remainder_contributions( +// evaluations.clone(), +// remainder_contributions.clone(), +// final_price, +// true, +// ); +// let expected_bid_payouts = +// MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, final_price); +// let expected_community_contribution_payouts = +// MockInstantiator::calculate_contributed_plmc_spent(community_contributions.clone(), final_price); +// let all_expected_payouts = MockInstantiator::generic_map_operation( +// vec![ +// expected_evaluators_and_contributors_payouts.clone(), +// expected_bid_payouts, +// expected_community_contribution_payouts, +// ], +// MergeOperation::Add, +// ); +// println!("all expected payouts {:?}", all_expected_payouts); +// for payout in all_expected_payouts.clone() { +// let evaluation_hold = inst.execute(|| { +// <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( +// &HoldReason::Evaluation(project_id).into(), +// &payout.account, +// ) +// }); +// let participation_hold = inst.execute(|| { +// <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( +// &HoldReason::Participation(project_id).into(), +// &payout.account, +// ) +// }); +// println!("account {:?} has evaluation hold {:?}", payout.account, evaluation_hold); +// println!("account {:?} has participation hold {:?}", payout.account, participation_hold); +// } +// let _deposit_required = <::ContributionTokenCurrency as AccountTouch< +// ProjectId, +// AccountIdOf, +// >>::deposit_required(project_id); + +// let prev_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; +// let all_participants = all_expected_payouts.accounts(); +// let prev_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants.clone()); + +// call_and_is_ok!( +// inst, +// Pallet::::decide_project_outcome( +// RuntimeOrigin::signed(issuer), +// project_id, +// FundingOutcomeDecision::RejectFunding +// ) +// ); +// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); +// assert_eq!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Failure(CleanerState::Initialized(PhantomData)) +// ); + +// let stored_evaluations = +// inst.execute(|| Evaluations::::iter_prefix_values((project_id,)).collect::>()); +// let stored_bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) +// .collect::>() +// }); +// let stored_contributions = +// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); + +// for evaluation in stored_evaluations { +// call_and_is_ok!( +// inst, +// Pallet::::evaluation_slash_for( +// RuntimeOrigin::signed(evaluation.evaluator), +// project_id, +// evaluation.evaluator, +// evaluation.id, +// ), +// Pallet::::evaluation_unbond_for( +// RuntimeOrigin::signed(evaluation.evaluator), +// project_id, +// evaluation.evaluator, +// evaluation.id, +// ) +// ) +// } + +// for bid in stored_bids { +// call_and_is_ok!( +// inst, +// Pallet::::release_bid_funds_for( +// RuntimeOrigin::signed(issuer), +// project_id, +// bid.bidder, +// bid.id, +// ), +// Pallet::::bid_unbond_for( +// RuntimeOrigin::signed(bid.bidder), +// project_id, +// bid.bidder, +// bid.id, +// ) +// ) +// } + +// for contribution in stored_contributions { +// call_and_is_ok!( +// inst, +// Pallet::::release_contribution_funds_for( +// RuntimeOrigin::signed(issuer), +// project_id, +// contribution.contributor, +// contribution.id, +// ), +// Pallet::::contribution_unbond_for( +// RuntimeOrigin::signed(contribution.contributor), +// project_id, +// contribution.contributor, +// contribution.id, +// ) +// ) +// } + +// let post_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; +// let post_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants); + +// let all_participants_plmc_deltas = MockInstantiator::generic_map_operation( +// vec![post_participants_plmc_balances, prev_participants_plmc_balances], +// MergeOperation::Subtract, +// ); + +// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; +// assert_eq!( +// inst.get_project_details(project_id).cleanup, +// Cleaner::Failure(CleanerState::Initialized(PhantomData)) +// ); +// assert_eq!(issuer_funding_delta, 0); +// assert_eq!(all_participants_plmc_deltas, all_expected_payouts); +// } + +// // i.e consumer increase bug fixed with touch on pallet-assets +// #[test] +// fn no_limit_on_project_contributions_per_user() { +// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + +// let project = |x| TestProjectParams { +// expected_state: ProjectStatus::FundingSuccessful, +// metadata: default_project_metadata(x, ISSUER), +// evaluations: default_evaluations(), +// bids: default_bids(), +// community_contributions: default_community_buys(), +// remainder_contributions: default_remainder_buys(), +// issuer: ISSUER, +// }; +// let projects = (0..20).into_iter().map(|x| project(x)).collect_vec(); +// async_features::create_multiple_projects_at(inst, projects); +// } + +// #[test] +// fn evaluation_plmc_unbonded_after_funding_success() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let evaluations = default_evaluations(); +// let evaluators = evaluations.accounts(); + +// let project_id = inst.create_remainder_contributing_project( +// default_project_metadata(inst.get_new_nonce(), ISSUER), +// ISSUER, +// evaluations.clone(), +// default_bids(), +// default_community_buys(), +// ); + +// let prev_reserved_plmc = +// inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Evaluation(project_id).into()); + +// let prev_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); + +// inst.finish_funding(project_id).unwrap(); +// inst.advance_time(::ManualAcceptanceDuration::get() + 1).unwrap(); +// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); +// inst.advance_time(10).unwrap(); +// let post_unbond_amounts: Vec> = prev_reserved_plmc +// .iter() +// .map(|UserToPLMCBalance { account, .. }| UserToPLMCBalance::new(*account, Zero::zero())) +// .collect(); + +// inst.do_reserved_plmc_assertions(post_unbond_amounts.clone(), HoldReason::Evaluation(project_id).into()); +// inst.do_reserved_plmc_assertions(post_unbond_amounts, HoldReason::Participation(project_id).into()); + +// let post_free_plmc = inst.get_free_plmc_balances_for(evaluators); + +// let increased_amounts = +// MockInstantiator::generic_map_operation(vec![post_free_plmc, prev_free_plmc], MergeOperation::Subtract); + +// assert_eq!(increased_amounts, MockInstantiator::calculate_evaluation_plmc_spent(evaluations)) +// } + +// #[test] +// fn plmc_vesting_schedule_starts_automatically() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let mut bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids.clone(), +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); + +// let price = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); +// bids = +// stored_bids.into_iter().map(|bid| BidParams::new_with_defaults(bid.bidder, bid.final_ct_amount)).collect(); +// let auction_locked_plmc = MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, price); +// let community_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(community_contributions, price); +// let remainder_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(remainder_contributions, price); +// let all_plmc_locks = MockInstantiator::generic_map_operation( +// vec![auction_locked_plmc, community_locked_plmc, remainder_locked_plmc], +// MergeOperation::Add, +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + +// inst.advance_time(10u64).unwrap(); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); + +// for UserToPLMCBalance { account, plmc_amount } in all_plmc_locks { +// let schedule = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &account, +// HoldReason::Participation(project_id).into(), +// ) +// }); + +// assert_eq!(schedule.unwrap(), plmc_amount); +// } +// } + +// #[test] +// fn plmc_vesting_schedule_starts_manually() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids.clone(), +// community_contributions.clone(), +// remainder_contributions.clone(), +// ); + +// let price = inst.get_project_details(project_id).weighted_average_price.unwrap(); + +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); + +// let stored_successful_bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) +// .collect::>() +// }); +// let stored_contributions = +// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); + +// for bid in stored_successful_bids.clone() { +// call_and_is_ok!( +// inst, +// Pallet::::do_start_bid_vesting_schedule_for(&bid.bidder, project_id, &bid.bidder, bid.id,) +// ); +// } +// for contribution in stored_contributions { +// call_and_is_ok!( +// inst, +// Pallet::::do_start_contribution_vesting_schedule_for( +// &contribution.contributor, +// project_id, +// &contribution.contributor, +// contribution.id, +// ) +// ); +// } + +// let auction_locked_plmc = MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, price); +// let community_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(community_contributions, price); +// let remainder_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(remainder_contributions, price); +// let all_plmc_locks = MockInstantiator::generic_map_operation( +// vec![auction_locked_plmc, community_locked_plmc, remainder_locked_plmc], +// MergeOperation::Add, +// ); + +// for UserToPLMCBalance { account, plmc_amount } in all_plmc_locks { +// let schedule = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &account, +// HoldReason::Participation(project_id).into(), +// ) +// }); + +// assert_eq!(schedule.unwrap(), plmc_amount); +// } +// } + +// #[test] +// fn plmc_vesting_full_amount() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids, +// community_contributions, +// remainder_contributions, +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + +// inst.advance_time(10u64).unwrap(); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); + +// let stored_successful_bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) +// .collect::>() +// }); + +// let stored_contributions = +// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); + +// let total_bid_plmc_in_vesting: Vec> = stored_successful_bids +// .iter() +// .map(|bid| (bid.bidder, bid.plmc_vesting_info.unwrap().total_amount).into()) +// .collect_vec(); + +// let total_contribution_plmc_in_vesting: Vec> = stored_contributions +// .iter() +// .map(|contribution| (contribution.contributor, contribution.plmc_vesting_info.unwrap().total_amount).into()) +// .collect_vec(); + +// let total_participant_plmc_in_vesting = MockInstantiator::generic_map_operation( +// vec![total_bid_plmc_in_vesting, total_contribution_plmc_in_vesting], +// MergeOperation::Add, +// ); + +// inst.advance_time((10 * DAYS).into()).unwrap(); + +// for UserToPLMCBalance { account, plmc_amount } in total_participant_plmc_in_vesting { +// let prev_free_balance = inst.execute(|| ::NativeCurrency::balance(&account)); + +// inst.execute(|| Pallet::::do_vest_plmc_for(account, project_id, account)).unwrap(); + +// let post_free_balance = inst.execute(|| ::NativeCurrency::balance(&account)); +// assert_eq!(plmc_amount, post_free_balance - prev_free_balance); +// } +// } + +// #[test] +// fn plmc_vesting_partial_amount() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER; +// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_contributions = default_community_buys(); +// let remainder_contributions = default_remainder_buys(); + +// let project_id = inst.create_finished_project( +// project_metadata, +// issuer, +// evaluations, +// bids, +// community_contributions, +// remainder_contributions, +// ); + +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.advance_time(15u64).unwrap(); +// let details = inst.get_project_details(project_id); +// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); +// let vest_start_block = details.funding_end_block.unwrap(); +// let stored_successful_bids = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) +// .collect::>() +// }); +// let stored_contributions = +// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); + +// let bidder_vesting = +// stored_successful_bids.iter().map(|bid| (bid.bidder, bid.plmc_vesting_info.unwrap())).collect_vec(); +// let contributor_vesting = stored_contributions +// .iter() +// .map(|contribution| (contribution.contributor, contribution.plmc_vesting_info.unwrap())) +// .collect_vec(); + +// let participant_vesting_infos: Vec<(AccountIdOf, Vec>)> = +// MockInstantiator::generic_map_merge_reduce( +// vec![bidder_vesting, contributor_vesting], +// |map| map.0, +// Vec::new(), +// |map, mut vestings| { +// vestings.push(map.1); +// vestings +// }, +// ); + +// let now = inst.current_block(); +// for (participant, vesting_infos) in participant_vesting_infos { +// let vested_amount = vesting_infos.into_iter().fold(0u128, |acc, vesting_info| { +// acc + vesting_info.amount_per_block * min(vesting_info.duration, now - vest_start_block) as u128 +// }); + +// let prev_free_balance = inst.execute(|| ::NativeCurrency::balance(&participant)); + +// inst.execute(|| Pallet::::do_vest_plmc_for(participant, project_id, participant)).unwrap(); + +// let post_free_balance = inst.execute(|| ::NativeCurrency::balance(&participant)); +// assert_eq!(vested_amount, post_free_balance - prev_free_balance); +// } +// } + +// #[test] +// fn ct_treasury_mints() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + +// let treasury_account = ::ContributionTreasury::get(); + +// let project_metadata = ProjectMetadataOf:: { +// token_information: default_token_information(), +// mainnet_token_max_supply: 8_000_000 * ASSET_UNIT, +// total_allocation_size: 1_000_000 * ASSET_UNIT, +// auction_round_allocation_percentage: Percent::from_percent(50u8), +// minimum_price: PriceOf::::from_float(10.0), +// bidding_ticket_sizes: BiddingTicketSizes { +// professional: TicketSize::new(Some(5000 * US_DOLLAR), None), +// institutional: TicketSize::new(Some(5000 * US_DOLLAR), None), +// phantom: Default::default(), +// }, +// contributing_ticket_sizes: ContributingTicketSizes { +// retail: TicketSize::new(None, None), +// professional: TicketSize::new(None, None), +// institutional: TicketSize::new(None, None), +// phantom: Default::default(), +// }, +// participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), +// funding_destination_account: ISSUER, +// offchain_information_hash: Some(hashed(METADATA)), +// }; +// let mut counter: u8 = 0u8; +// let mut with_different_metadata = |mut project: ProjectMetadataOf| { +// let mut binding = project.offchain_information_hash.unwrap(); +// let h256_bytes = binding.as_fixed_bytes_mut(); +// h256_bytes[0] = counter; +// counter += 1u8; +// project.offchain_information_hash = Some(binding); +// project +// }; + +// let price = project_metadata.minimum_price; + +// // Failed project has no mints on the treasury +// let project_20_percent = inst.create_finished_project( +// with_different_metadata(project_metadata.clone()), +// ISSUER, +// default_evaluations(), +// default_bids_from_ct_percent(10), +// default_community_contributions_from_ct_percent(10), +// vec![], +// ); +// inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); +// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); +// let ct_balance = inst.execute(|| { +// ::ContributionTokenCurrency::balance(project_20_percent, treasury_account) +// }); +// assert_eq!(ct_balance, 0); + +// // 50% funded project can have mints on the treasury if issuer accepts or enough time passes for automatic acceptance +// let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; +// let fee_8_percent = Percent::from_percent(8) * 4_000_000 * US_DOLLAR; +// let fee_6_percent = Percent::from_percent(6) * 0 * US_DOLLAR; +// let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; +// let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); + +// let project_50_percent = inst.create_finished_project( +// with_different_metadata(project_metadata.clone()), +// ISSUER, +// default_evaluations(), +// default_bids_from_ct_percent(25), +// default_community_contributions_from_ct_percent(20), +// default_remainder_contributions_from_ct_percent(5), +// ); +// inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); +// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); +// let ct_balance = inst.execute(|| { +// ::ContributionTokenCurrency::balance(project_50_percent, treasury_account) +// }); +// let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; +// let expected_long_term_holder_bonus_minted = Percent::from_percent(50) * total_ct_fee; +// assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); + +// // 80% funded project can have mints on the treasury if issuer accepts or enough time passes for automatic acceptance +// let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; +// let fee_8_percent = Percent::from_percent(8) * 5_000_000 * US_DOLLAR; +// let fee_6_percent = Percent::from_percent(6) * 2_000_000 * US_DOLLAR; +// let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; +// let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); + +// let project_80_percent = inst.create_finished_project( +// with_different_metadata(project_metadata.clone()), +// ISSUER, +// default_evaluations(), +// default_bids_from_ct_percent(40), +// default_community_contributions_from_ct_percent(30), +// default_remainder_contributions_from_ct_percent(10), +// ); +// inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); +// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); +// let ct_balance = inst.execute(|| { +// ::ContributionTokenCurrency::balance(project_80_percent, treasury_account) +// }); +// let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; +// let expected_long_term_holder_bonus_minted = Percent::from_percent(50) * total_ct_fee; +// assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); + +// // 98% funded project always has mints on the treasury +// let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; +// let fee_8_percent = Percent::from_percent(8) * 5_000_000 * US_DOLLAR; +// let fee_6_percent = Percent::from_percent(6) * 3_800_000 * US_DOLLAR; +// let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; +// let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); + +// let project_98_percent = inst.create_finished_project( +// with_different_metadata(project_metadata.clone()), +// ISSUER, +// default_evaluations(), +// default_bids_from_ct_percent(49), +// default_community_contributions_from_ct_percent(39), +// default_remainder_contributions_from_ct_percent(10), +// ); +// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); +// let ct_balance = inst.execute(|| { +// ::ContributionTokenCurrency::balance(project_98_percent, treasury_account) +// }); +// let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; +// let lthb_percent = Perquintill::from_percent(20) + Perquintill::from_percent(30) * Perquintill::from_percent(2); +// let expected_long_term_holder_bonus_minted = lthb_percent * total_ct_fee; +// assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); + +// // Test the touch on the treasury ct account by the issuer. +// // We create more CT accounts that the account can use provider references for, +// // so if it succeeds, then it means the touch was successful. +// let consumer_limit: u32 = ::MaxConsumers::get(); + +// // we want to test ct mints on treasury of 1 over the consumer limit, +// // and we already minted 3 contribution tokens on previous tests. +// for i in 0..consumer_limit + 1u32 - 3u32 { +// let _project_98_percent = inst.create_finished_project( +// with_different_metadata(project_metadata.clone()), +// ISSUER + i + 1000, +// default_evaluations(), +// default_bids_from_ct_percent(49), +// default_community_contributions_from_ct_percent(39), +// default_remainder_contributions_from_ct_percent(10), +// ); +// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); +// } +// } +// } // only functionalities related to the CT Migration mod ct_migration { diff --git a/pallets/funding/src/weights.rs b/pallets/funding/src/weights.rs index aa2b0b9a2..88dbbb476 100644 --- a/pallets/funding/src/weights.rs +++ b/pallets/funding/src/weights.rs @@ -58,23 +58,13 @@ pub trait WeightInfo { fn bid(x: u32, y: u32, ) -> Weight; fn contribution(x: u32, ) -> Weight; fn contribution_ends_round(x: u32, y: u32 ) -> Weight; - fn evaluation_unbond_for() -> Weight; - fn evaluation_reward_payout_for_with_ct_account_creation() -> Weight; - fn evaluation_reward_payout_for_no_ct_account_creation() -> Weight; - fn evaluation_slash_for() -> Weight; - fn bid_ct_mint_for_with_ct_account_creation() -> Weight; - fn bid_ct_mint_for_no_ct_account_creation() -> Weight; - fn contribution_ct_mint_for_with_ct_account_creation() -> Weight; - fn contribution_ct_mint_for_no_ct_account_creation() -> Weight; - fn start_bid_vesting_schedule_for() -> Weight; - fn start_contribution_vesting_schedule_for() -> Weight; - fn payout_bid_funds_for() -> Weight; - fn payout_contribution_funds_for() -> Weight; fn decide_project_outcome(x: u32 ) -> Weight; - fn release_bid_funds_for() -> Weight; - fn release_contribution_funds_for() -> Weight; - fn bid_unbond_for() -> Weight; - fn contribution_unbond_for() -> Weight; + fn settle_successful_evaluation() -> Weight; + fn settle_failed_evaluation() -> Weight; + fn settle_successful_bid() -> Weight; + fn settle_failed_bid() -> Weight; + fn settle_successful_contribution() -> Weight; + fn settle_failed_contribution() -> Weight; fn end_evaluation_success(x: u32, ) -> Weight; fn end_evaluation_failure() -> Weight; fn start_candle_phase(x: u32, ) -> Weight; @@ -329,214 +319,6 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Evaluations` (r:1 w:1) - /// Proof: `PolimecFunding::Evaluations` (`max_values`: None, `max_size`: Some(345), added: 2820, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn evaluation_unbond_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1477` - // Estimated: `4614` - // Minimum execution time: 54_000_000 picoseconds. - Weight::from_parts(57_000_000, 4614) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Evaluations` (r:1 w:1) - /// Proof: `PolimecFunding::Evaluations` (`max_values`: None, `max_size`: Some(345), added: 2820, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn evaluation_reward_payout_for_with_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1654` - // Estimated: `4614` - // Minimum execution time: 94_000_000 picoseconds. - Weight::from_parts(98_000_000, 4614) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Evaluations` (r:1 w:1) - /// Proof: `PolimecFunding::Evaluations` (`max_values`: None, `max_size`: Some(345), added: 2820, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn evaluation_reward_payout_for_no_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1322` - // Estimated: `3814` - // Minimum execution time: 48_000_000 picoseconds. - Weight::from_parts(50_000_000, 3814) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Evaluations` (r:1 w:1) - /// Proof: `PolimecFunding::Evaluations` (`max_values`: None, `max_size`: Some(345), added: 2820, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn evaluation_slash_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1485` - // Estimated: `4614` - // Minimum execution time: 67_000_000 picoseconds. - Weight::from_parts(69_000_000, 4614) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn bid_ct_mint_for_with_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1735` - // Estimated: `4614` - // Minimum execution time: 99_000_000 picoseconds. - Weight::from_parts(103_000_000, 4614) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn bid_ct_mint_for_no_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1250` - // Estimated: `3883` - // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(50_000_000, 3883) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn contribution_ct_mint_for_with_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1651` - // Estimated: `4614` - // Minimum execution time: 94_000_000 picoseconds. - Weight::from_parts(100_000_000, 4614) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn contribution_ct_mint_for_no_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1287` - // Estimated: `3829` - // Minimum execution time: 47_000_000 picoseconds. - Weight::from_parts(50_000_000, 3829) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `LinearRelease::Vesting` (r:1 w:1) - /// Proof: `LinearRelease::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn start_bid_vesting_schedule_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `851` - // Estimated: `4316` - // Minimum execution time: 29_000_000 picoseconds. - Weight::from_parts(31_000_000, 4316) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `LinearRelease::Vesting` (r:1 w:1) - /// Proof: `LinearRelease::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn start_contribution_vesting_schedule_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `880` - // Estimated: `4345` - // Minimum execution time: 30_000_000 picoseconds. - Weight::from_parts(31_000_000, 4345) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Asset` (r:1 w:1) - /// Proof: `StatemintAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Account` (r:2 w:2) - /// Proof: `StatemintAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn payout_bid_funds_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1405` - // Estimated: `6208` - // Minimum execution time: 67_000_000 picoseconds. - Weight::from_parts(68_000_000, 6208) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Asset` (r:1 w:1) - /// Proof: `StatemintAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Account` (r:2 w:2) - /// Proof: `StatemintAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn payout_contribution_funds_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1434` - // Estimated: `6208` - // Minimum execution time: 66_000_000 picoseconds. - Weight::from_parts(69_000_000, 6208) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) /// Storage: `PolimecFunding::ProjectsToUpdate` (r:2980 w:1) /// Proof: `PolimecFunding::ProjectsToUpdate` (`max_values`: None, `max_size`: Some(27), added: 2502, mode: `MaxEncodedLen`) /// The range of component `x` is `[1, 99]`. @@ -555,69 +337,24 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 3946).saturating_mul(x.into())) } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Asset` (r:1 w:1) - /// Proof: `StatemintAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Account` (r:2 w:2) - /// Proof: `StatemintAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn release_bid_funds_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1514` - // Estimated: `6208` - // Minimum execution time: 65_000_000 picoseconds. - Weight::from_parts(70_000_000, 6208) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + + fn settle_successful_evaluation() -> Weight { + Weight::from_parts(0, 0) } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Asset` (r:1 w:1) - /// Proof: `StatemintAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Account` (r:2 w:2) - /// Proof: `StatemintAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn release_contribution_funds_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1507` - // Estimated: `6208` - // Minimum execution time: 63_000_000 picoseconds. - Weight::from_parts(69_000_000, 6208) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + fn settle_failed_evaluation() -> Weight { + Weight::from_parts(0, 0) } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn bid_unbond_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1519` - // Estimated: `4614` - // Minimum execution time: 54_000_000 picoseconds. - Weight::from_parts(56_000_000, 4614) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + fn settle_successful_bid() -> Weight { + Weight::from_parts(0, 0) } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn contribution_unbond_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1474` - // Estimated: `4614` - // Minimum execution time: 54_000_000 picoseconds. - Weight::from_parts(57_000_000, 4614) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + fn settle_failed_bid() -> Weight { + Weight::from_parts(0, 0) + } + fn settle_successful_contribution() -> Weight { + Weight::from_parts(0, 0) + } + fn settle_failed_contribution() -> Weight { + Weight::from_parts(0, 0) } /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:1) /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) @@ -1152,214 +889,6 @@ impl WeightInfo for () { } /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Evaluations` (r:1 w:1) - /// Proof: `PolimecFunding::Evaluations` (`max_values`: None, `max_size`: Some(345), added: 2820, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn evaluation_unbond_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1477` - // Estimated: `4614` - // Minimum execution time: 54_000_000 picoseconds. - Weight::from_parts(57_000_000, 4614) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Evaluations` (r:1 w:1) - /// Proof: `PolimecFunding::Evaluations` (`max_values`: None, `max_size`: Some(345), added: 2820, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn evaluation_reward_payout_for_with_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1654` - // Estimated: `4614` - // Minimum execution time: 94_000_000 picoseconds. - Weight::from_parts(98_000_000, 4614) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Evaluations` (r:1 w:1) - /// Proof: `PolimecFunding::Evaluations` (`max_values`: None, `max_size`: Some(345), added: 2820, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - fn evaluation_reward_payout_for_no_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1322` - // Estimated: `3814` - // Minimum execution time: 48_000_000 picoseconds. - Weight::from_parts(50_000_000, 3814) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Evaluations` (r:1 w:1) - /// Proof: `PolimecFunding::Evaluations` (`max_values`: None, `max_size`: Some(345), added: 2820, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn evaluation_slash_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1485` - // Estimated: `4614` - // Minimum execution time: 67_000_000 picoseconds. - Weight::from_parts(69_000_000, 4614) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn bid_ct_mint_for_with_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1735` - // Estimated: `4614` - // Minimum execution time: 99_000_000 picoseconds. - Weight::from_parts(103_000_000, 4614) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn bid_ct_mint_for_no_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1250` - // Estimated: `3883` - // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(50_000_000, 3883) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn contribution_ct_mint_for_with_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1651` - // Estimated: `4614` - // Minimum execution time: 94_000_000 picoseconds. - Weight::from_parts(100_000_000, 4614) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Asset` (r:1 w:1) - /// Proof: `LocalAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `LocalAssets::Account` (r:1 w:1) - /// Proof: `LocalAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn contribution_ct_mint_for_no_ct_account_creation() -> Weight { - // Proof Size summary in bytes: - // Measured: `1287` - // Estimated: `3829` - // Minimum execution time: 47_000_000 picoseconds. - Weight::from_parts(50_000_000, 3829) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `LinearRelease::Vesting` (r:1 w:1) - /// Proof: `LinearRelease::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn start_bid_vesting_schedule_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `851` - // Estimated: `4316` - // Minimum execution time: 29_000_000 picoseconds. - Weight::from_parts(31_000_000, 4316) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `LinearRelease::Vesting` (r:1 w:1) - /// Proof: `LinearRelease::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn start_contribution_vesting_schedule_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `880` - // Estimated: `4345` - // Minimum execution time: 30_000_000 picoseconds. - Weight::from_parts(31_000_000, 4345) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Asset` (r:1 w:1) - /// Proof: `StatemintAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Account` (r:2 w:2) - /// Proof: `StatemintAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn payout_bid_funds_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1405` - // Estimated: `6208` - // Minimum execution time: 67_000_000 picoseconds. - Weight::from_parts(68_000_000, 6208) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Asset` (r:1 w:1) - /// Proof: `StatemintAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Account` (r:2 w:2) - /// Proof: `StatemintAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn payout_contribution_funds_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1434` - // Estimated: `6208` - // Minimum execution time: 66_000_000 picoseconds. - Weight::from_parts(69_000_000, 6208) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) /// Storage: `PolimecFunding::ProjectsToUpdate` (r:2980 w:1) /// Proof: `PolimecFunding::ProjectsToUpdate` (`max_values`: None, `max_size`: Some(27), added: 2502, mode: `MaxEncodedLen`) /// The range of component `x` is `[1, 99]`. @@ -1378,69 +907,23 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 3946).saturating_mul(x.into())) } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Asset` (r:1 w:1) - /// Proof: `StatemintAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Account` (r:2 w:2) - /// Proof: `StatemintAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn release_bid_funds_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1514` - // Estimated: `6208` - // Minimum execution time: 65_000_000 picoseconds. - Weight::from_parts(70_000_000, 6208) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + fn settle_successful_evaluation() -> Weight { + Weight::from_parts(0, 0) } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Asset` (r:1 w:1) - /// Proof: `StatemintAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `StatemintAssets::Account` (r:2 w:2) - /// Proof: `StatemintAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn release_contribution_funds_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1507` - // Estimated: `6208` - // Minimum execution time: 63_000_000 picoseconds. - Weight::from_parts(69_000_000, 6208) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + fn settle_failed_evaluation() -> Weight { + Weight::from_parts(0, 0) } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Bids` (r:1 w:1) - /// Proof: `PolimecFunding::Bids` (`max_values`: None, `max_size`: Some(418), added: 2893, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn bid_unbond_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1519` - // Estimated: `4614` - // Minimum execution time: 54_000_000 picoseconds. - Weight::from_parts(56_000_000, 4614) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + fn settle_successful_bid() -> Weight { + Weight::from_parts(0, 0) } - /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:0) - /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) - /// Storage: `PolimecFunding::Contributions` (r:1 w:1) - /// Proof: `PolimecFunding::Contributions` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(1149), added: 3624, mode: `MaxEncodedLen`) - fn contribution_unbond_for() -> Weight { - // Proof Size summary in bytes: - // Measured: `1474` - // Estimated: `4614` - // Minimum execution time: 54_000_000 picoseconds. - Weight::from_parts(57_000_000, 4614) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + fn settle_failed_bid() -> Weight { + Weight::from_parts(0, 0) + } + fn settle_successful_contribution() -> Weight { + Weight::from_parts(0, 0) + } + fn settle_failed_contribution() -> Weight { + Weight::from_parts(0, 0) } /// Storage: `PolimecFunding::ProjectsDetails` (r:1 w:1) /// Proof: `PolimecFunding::ProjectsDetails` (`max_values`: None, `max_size`: Some(349), added: 2824, mode: `MaxEncodedLen`) From cc5179781687d9eb342bd09705398a9fecd16a5b Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Thu, 21 Mar 2024 16:27:32 +0100 Subject: [PATCH 08/21] remove cleaner --- pallets/funding/src/impls.rs | 722 ----------------------------------- 1 file changed, 722 deletions(-) delete mode 100644 pallets/funding/src/impls.rs diff --git a/pallets/funding/src/impls.rs b/pallets/funding/src/impls.rs deleted file mode 100644 index 27d43748d..000000000 --- a/pallets/funding/src/impls.rs +++ /dev/null @@ -1,722 +0,0 @@ -// Polimec Blockchain – https://www.polimec.org/ -// Copyright (C) Polimec 2022. All rights reserved. - -// The Polimec Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The Polimec Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// use frame_support::{ -// dispatch::{DispatchErrorWithPostInfo, GetDispatchInfo}, -// traits::Get, -// weights::Weight, -// }; -// use sp_arithmetic::traits::Zero; -// use sp_runtime::{traits::AccountIdConversion, DispatchError}; -// use sp_std::marker::PhantomData; - -// use crate::{traits::DoRemainingOperation, *}; - -// impl DoRemainingOperation for Cleaner { -// fn has_remaining_operations(&self) -> bool { -// match self { -// Cleaner::NotReady => false, -// Cleaner::Success(state) => -// as DoRemainingOperation>::has_remaining_operations(state), -// Cleaner::Failure(state) => -// as DoRemainingOperation>::has_remaining_operations(state), -// } -// } - -// fn do_one_operation(&mut self, project_id: ProjectId) -> Result { -// match self { -// Cleaner::NotReady => Err(DispatchError::Other("Cleaner not ready")), -// Cleaner::Success(state) => -// as DoRemainingOperation>::do_one_operation(state, project_id), -// Cleaner::Failure(state) => -// as DoRemainingOperation>::do_one_operation(state, project_id), -// } -// } -// } - -// impl DoRemainingOperation for CleanerState { -// fn has_remaining_operations(&self) -> bool { -// !matches!(self, CleanerState::Finished(_)) -// } - -// fn do_one_operation(&mut self, project_id: ProjectId) -> Result { -// let evaluators_outcome = ProjectsDetails::::get(project_id) -// .ok_or(Error::::ImpossibleState)? -// .evaluation_round_info -// .evaluators_outcome; -// let base_weight = Weight::from_parts(10_000_000, 0); - -// match self { -// CleanerState::Initialized(PhantomData) => { -// *self = Self::EvaluationRewardOrSlash( -// remaining_evaluators_to_reward_or_slash::(project_id, evaluators_outcome), -// PhantomData, -// ); -// Ok(base_weight) -// }, -// CleanerState::EvaluationRewardOrSlash(remaining, PhantomData) => -// if *remaining == 0 { -// *self = Self::EvaluationUnbonding(remaining_evaluations::(project_id), PhantomData); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_evaluations) = -// reward_or_slash_one_evaluation::(project_id).map_err(|error_info| error_info.error)?; -// *self = CleanerState::EvaluationRewardOrSlash(remaining_evaluations, PhantomData); -// Ok(consumed_weight) -// }, -// CleanerState::EvaluationUnbonding(remaining, PhantomData) => -// if *remaining == 0 { -// *self = CleanerState::StartBidderVestingSchedule( -// remaining_successful_bids::(project_id), -// PhantomData, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_evaluations) = unbond_one_evaluation::(project_id); -// *self = CleanerState::EvaluationUnbonding(remaining_evaluations, PhantomData); -// Ok(consumed_weight) -// }, -// CleanerState::StartBidderVestingSchedule(remaining, PhantomData) => -// if *remaining == 0 { -// *self = CleanerState::StartContributorVestingSchedule( -// remaining_contributions::(project_id), -// PhantomData, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_evaluations) = start_one_bid_vesting_schedule::(project_id); -// *self = CleanerState::StartBidderVestingSchedule(remaining_evaluations, PhantomData); -// Ok(consumed_weight) -// }, -// CleanerState::StartContributorVestingSchedule(remaining, PhantomData) => -// if *remaining == 0 { -// *self = CleanerState::BidCTMint(remaining_bids_without_ct_minted::(project_id), PhantomData); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_evaluations) = -// start_one_contribution_vesting_schedule::(project_id); -// *self = CleanerState::StartContributorVestingSchedule(remaining_evaluations, PhantomData); -// Ok(consumed_weight) -// }, -// CleanerState::BidCTMint(remaining, PhantomData) => -// if *remaining == 0 { -// *self = CleanerState::ContributionCTMint( -// remaining_contributions_without_ct_minted::(project_id), -// PhantomData, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_bids) = mint_ct_for_one_bid::(project_id); -// *self = CleanerState::BidCTMint(remaining_bids, PhantomData); -// Ok(consumed_weight) -// }, -// CleanerState::ContributionCTMint(remaining, PhantomData) => -// if *remaining == 0 { -// *self = CleanerState::BidFundingPayout( -// remaining_bids_without_issuer_payout::(project_id), -// PhantomData, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_contributions) = mint_ct_for_one_contribution::(project_id); -// *self = CleanerState::ContributionCTMint(remaining_contributions, PhantomData); -// Ok(consumed_weight) -// }, -// CleanerState::BidFundingPayout(remaining, PhantomData) => -// if *remaining == 0 { -// *self = CleanerState::ContributionFundingPayout( -// remaining_contributions_without_issuer_payout::(project_id), -// PhantomData, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_contributions) = issuer_funding_payout_one_bid::(project_id); -// *self = CleanerState::BidFundingPayout(remaining_contributions, PhantomData); -// Ok(consumed_weight) -// }, -// CleanerState::ContributionFundingPayout(remaining, PhantomData) => -// if *remaining == 0 { -// *self = CleanerState::Finished(PhantomData); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_contributions) = -// issuer_funding_payout_one_contribution::(project_id); -// *self = CleanerState::ContributionFundingPayout(remaining_contributions, PhantomData); -// Ok(consumed_weight) -// }, -// CleanerState::Finished(PhantomData) => Err(Error::::FinalizerFinished.into()), - -// _ => Err(Error::::ImpossibleState.into()), -// } -// } -// } -// impl DoRemainingOperation for CleanerState { -// fn has_remaining_operations(&self) -> bool { -// !matches!(self, CleanerState::Finished(PhantomData::)) -// } - -// fn do_one_operation(&mut self, project_id: ProjectId) -> Result { -// let evaluators_outcome = ProjectsDetails::::get(project_id) -// .ok_or(Error::::ImpossibleState)? -// .evaluation_round_info -// .evaluators_outcome; -// let base_weight = Weight::from_parts(10_000_000, 0); - -// match self { -// CleanerState::Initialized(PhantomData::) => { -// *self = CleanerState::EvaluationRewardOrSlash( -// remaining_evaluators_to_reward_or_slash::(project_id, evaluators_outcome), -// PhantomData::, -// ); -// Ok(base_weight) -// }, - -// CleanerState::EvaluationRewardOrSlash(remaining, PhantomData::) => -// if *remaining == 0 { -// *self = CleanerState::EvaluationUnbonding( -// remaining_evaluations::(project_id), -// PhantomData::, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_evaluators) = -// reward_or_slash_one_evaluation::(project_id).map_err(|error_info| error_info.error)?; -// *self = CleanerState::EvaluationRewardOrSlash(remaining_evaluators, PhantomData); -// Ok(consumed_weight) -// }, - -// CleanerState::EvaluationUnbonding(remaining, PhantomData::) => -// if *remaining == 0 { -// *self = CleanerState::BidFundingRelease( -// remaining_bids_to_release_funds::(project_id), -// PhantomData::, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_evaluators) = unbond_one_evaluation::(project_id); -// *self = CleanerState::EvaluationUnbonding(remaining_evaluators, PhantomData); -// Ok(consumed_weight) -// }, - -// CleanerState::BidFundingRelease(remaining, PhantomData::) => -// if *remaining == 0 { -// *self = CleanerState::BidUnbonding(remaining_bids::(project_id), PhantomData::); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_bids) = release_funds_one_bid::(project_id); -// *self = CleanerState::BidFundingRelease(remaining_bids, PhantomData); -// Ok(consumed_weight) -// }, - -// CleanerState::BidUnbonding(remaining, PhantomData::) => -// if *remaining == 0 { -// *self = CleanerState::ContributionFundingRelease( -// remaining_contributions_to_release_funds::(project_id), -// PhantomData::, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_bids) = unbond_one_bid::(project_id); -// *self = CleanerState::BidUnbonding(remaining_bids, PhantomData::); -// Ok(consumed_weight) -// }, - -// CleanerState::ContributionFundingRelease(remaining, PhantomData::) => -// if *remaining == 0 { -// *self = CleanerState::ContributionUnbonding( -// remaining_contributions::(project_id), -// PhantomData::, -// ); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_contributions) = release_funds_one_contribution::(project_id); -// *self = CleanerState::ContributionFundingRelease(remaining_contributions, PhantomData::); -// Ok(consumed_weight) -// }, - -// CleanerState::ContributionUnbonding(remaining, PhantomData::) => -// if *remaining == 0 { -// *self = CleanerState::Finished(PhantomData::); -// Ok(base_weight) -// } else { -// let (consumed_weight, remaining_contributions) = unbond_one_contribution::(project_id); -// *self = CleanerState::ContributionUnbonding(remaining_contributions, PhantomData::); -// Ok(consumed_weight) -// }, - -// CleanerState::Finished(PhantomData::) => Err(Error::::FinalizerFinished.into()), - -// _ => Err(Error::::ImpossibleState.into()), -// } -// } -// } - -// fn remaining_evaluators_to_reward_or_slash(project_id: ProjectId, outcome: EvaluatorsOutcomeOf) -> u64 { -// if outcome == EvaluatorsOutcomeOf::::Unchanged { -// 0u64 -// } else { -// Evaluations::::iter_prefix_values((project_id,)) -// .filter(|evaluation| evaluation.rewarded_or_slashed.is_none()) -// .count() as u64 -// } -// } - -// fn remaining_evaluations(project_id: ProjectId) -> u64 { -// Evaluations::::iter_prefix_values((project_id,)).count() as u64 -// } - -// fn remaining_bids_to_release_funds(project_id: ProjectId) -> u64 { -// Bids::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 -// } - -// fn remaining_bids(project_id: ProjectId) -> u64 { -// Bids::::iter_prefix_values((project_id,)).count() as u64 -// } - -// fn remaining_successful_bids(project_id: ProjectId) -> u64 { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) -// .count() as u64 -// } - -// fn remaining_contributions_to_release_funds(project_id: ProjectId) -> u64 { -// Contributions::::iter_prefix_values((project_id,)).filter(|contribution| !contribution.funds_released).count() -// as u64 -// } - -// fn remaining_contributions(project_id: ProjectId) -> u64 { -// Contributions::::iter_prefix_values((project_id,)).count() as u64 -// } - -// fn remaining_bids_without_ct_minted(project_id: ProjectId) -> u64 { -// let project_bids = Bids::::iter_prefix_values((project_id,)); -// project_bids.filter(|bid| !bid.ct_minted).count() as u64 -// } - -// fn remaining_contributions_without_ct_minted(project_id: ProjectId) -> u64 { -// let project_contributions = Contributions::::iter_prefix_values((project_id,)); -// project_contributions.filter(|contribution| !contribution.ct_minted).count() as u64 -// } - -// fn remaining_bids_without_issuer_payout(project_id: ProjectId) -> u64 { -// Bids::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 -// } - -// fn remaining_contributions_without_issuer_payout(project_id: ProjectId) -> u64 { -// Contributions::::iter_prefix_values((project_id,)).filter(|bid| !bid.funds_released).count() as u64 -// } - -// fn reward_or_slash_one_evaluation( -// project_id: ProjectId, -// ) -> Result<(Weight, u64), DispatchErrorWithPostInfo> { -// let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectNotFound)?; -// let project_evaluations = Evaluations::::iter_prefix_values((project_id,)); -// let mut remaining_evaluations = project_evaluations.filter(|evaluation| evaluation.rewarded_or_slashed.is_none()); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(evaluation) = remaining_evaluations.next() { -// // TODO: This base weight and the one in all other functions below should be calculated with a benchmark -// let remaining = remaining_evaluations.count() as u64; -// match project_details.evaluation_round_info.evaluators_outcome { -// EvaluatorsOutcome::Rewarded(_) => { -// let mut weight_consumed = crate::Call::::evaluation_reward_payout_for { -// project_id: evaluation.project_id, -// evaluator: evaluation.evaluator.clone(), -// bond_id: evaluation.id, -// } -// .get_dispatch_info() -// .weight; - -// match Pallet::::do_evaluation_reward_payout_for( -// &T::PalletId::get().into_account_truncating(), -// evaluation.project_id, -// &evaluation.evaluator, -// evaluation.id, -// ) { -// Ok(result) => { -// if let Some(weight) = result.actual_weight { -// weight_consumed = weight -// }; -// }, -// Err(e) => { -// if let Some(weight) = e.post_info.actual_weight { -// weight_consumed = weight -// }; -// Pallet::::deposit_event(Event::EvaluationRewardFailed { -// project_id: evaluation.project_id, -// evaluator: evaluation.evaluator.clone(), -// id: evaluation.id, -// error: e.error, -// }) -// }, -// }; - -// Ok((base_weight.saturating_add(weight_consumed), remaining)) -// }, -// EvaluatorsOutcome::Slashed => { -// match Pallet::::do_evaluation_slash_for( -// &T::PalletId::get().into_account_truncating(), -// evaluation.project_id, -// &evaluation.evaluator, -// evaluation.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::EvaluationSlashFailed { -// project_id: evaluation.project_id, -// evaluator: evaluation.evaluator.clone(), -// id: evaluation.id, -// error: e, -// }), -// }; - -// Ok((base_weight.saturating_add(WeightInfoOf::::evaluation_slash_for()), remaining)) -// }, -// _ => { -// #[cfg(debug_assertions)] -// unreachable!("EvaluatorsOutcome should be either Slashed or Rewarded if this function is called"); -// #[cfg(not(debug_assertions))] -// Err(Error::::ImpossibleState.into()) -// }, -// } -// } else { -// Ok((base_weight, 0u64)) -// } -// } - -// fn unbond_one_evaluation(project_id: ProjectId) -> (Weight, u64) { -// let project_evaluations = Evaluations::::iter_prefix_values((project_id,)); -// let mut remaining_evaluations = -// project_evaluations.filter(|evaluation| evaluation.current_plmc_bond > Zero::zero()); -// let base_weight = Weight::from_parts(10_000_000, 0); -// if let Some(evaluation) = remaining_evaluations.next() { -// match Pallet::::do_evaluation_unbond_for( -// &T::PalletId::get().into_account_truncating(), -// evaluation.project_id, -// &evaluation.evaluator, -// evaluation.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::EvaluationUnbondFailed { -// project_id: evaluation.project_id, -// evaluator: evaluation.evaluator.clone(), -// id: evaluation.id, -// error: e, -// }), -// }; -// (base_weight.saturating_add(WeightInfoOf::::evaluation_unbond_for()), remaining_evaluations.count() as u64) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn release_funds_one_bid(project_id: ProjectId) -> (Weight, u64) { -// let project_bids = Bids::::iter_prefix_values((project_id,)); -// let mut remaining_bids = project_bids.filter(|bid| !bid.funds_released); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(bid) = remaining_bids.next() { -// match Pallet::::do_release_bid_funds_for( -// &T::PalletId::get().into_account_truncating(), -// bid.project_id, -// &bid.bidder, -// bid.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::ReleaseBidFundsFailed { -// project_id: bid.project_id, -// bidder: bid.bidder.clone(), -// id: bid.id, -// error: e, -// }), -// }; - -// (base_weight.saturating_add(WeightInfoOf::::release_bid_funds_for()), remaining_bids.count() as u64) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn unbond_one_bid(project_id: ProjectId) -> (Weight, u64) { -// let project_bids = Bids::::iter_prefix_values((project_id,)); -// let mut remaining_bids = project_bids.filter(|bid| bid.funds_released); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(bid) = remaining_bids.next() { -// match Pallet::::do_bid_unbond_for( -// &T::PalletId::get().into_account_truncating(), -// bid.project_id, -// &bid.bidder, -// bid.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::BidUnbondFailed { -// project_id: bid.project_id, -// bidder: bid.bidder.clone(), -// id: bid.id, -// error: e, -// }), -// }; -// (base_weight.saturating_add(WeightInfoOf::::bid_unbond_for()), remaining_bids.count() as u64) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn release_funds_one_contribution(project_id: ProjectId) -> (Weight, u64) { -// let project_contributions = Contributions::::iter_prefix_values((project_id,)); -// let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.funds_released); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(contribution) = remaining_contributions.next() { -// match Pallet::::do_release_contribution_funds_for( -// &T::PalletId::get().into_account_truncating(), -// contribution.project_id, -// &contribution.contributor, -// contribution.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::ReleaseContributionFundsFailed { -// project_id: contribution.project_id, -// contributor: contribution.contributor.clone(), -// id: contribution.id, -// error: e, -// }), -// }; - -// ( -// base_weight.saturating_add(WeightInfoOf::::release_contribution_funds_for()), -// remaining_contributions.count() as u64, -// ) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn unbond_one_contribution(project_id: ProjectId) -> (Weight, u64) { -// let project_contributions = Contributions::::iter_prefix_values((project_id,)); - -// let mut remaining_contributions = -// project_contributions.into_iter().filter(|contribution| contribution.funds_released); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(contribution) = remaining_contributions.next() { -// match Pallet::::do_contribution_unbond_for( -// &T::PalletId::get().into_account_truncating(), -// contribution.project_id, -// &contribution.contributor, -// contribution.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::ContributionUnbondFailed { -// project_id: contribution.project_id, -// contributor: contribution.contributor.clone(), -// id: contribution.id, -// error: e, -// }), -// }; -// ( -// base_weight.saturating_add(WeightInfoOf::::contribution_unbond_for()), -// remaining_contributions.count() as u64, -// ) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn start_one_bid_vesting_schedule(project_id: ProjectId) -> (Weight, u64) { -// let project_bids = Bids::::iter_prefix_values((project_id,)); -// let mut unscheduled_bids = project_bids.filter(|bid| { -// bid.plmc_vesting_info.is_none() && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) -// }); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(bid) = unscheduled_bids.next() { -// match Pallet::::do_start_bid_vesting_schedule_for( -// &T::PalletId::get().into_account_truncating(), -// project_id, -// &bid.bidder, -// bid.id, -// ) { -// Ok(_) => {}, -// Err(e) => { -// // TODO: Handle `MAX_VESTING_SCHEDULES` error - -// Pallet::::deposit_event(Event::StartBidderVestingScheduleFailed { -// project_id: bid.project_id, -// bidder: bid.bidder.clone(), -// id: bid.id, -// error: e, -// }); -// }, -// } -// ( -// base_weight.saturating_add(WeightInfoOf::::start_bid_vesting_schedule_for()), -// unscheduled_bids.count() as u64, -// ) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn start_one_contribution_vesting_schedule(project_id: ProjectId) -> (Weight, u64) { -// let project_bids = Contributions::::iter_prefix_values((project_id,)); -// let mut unscheduled_contributions = project_bids.filter(|contribution| contribution.plmc_vesting_info.is_none()); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(contribution) = unscheduled_contributions.next() { -// match Pallet::::do_start_contribution_vesting_schedule_for( -// &T::PalletId::get().into_account_truncating(), -// project_id, -// &contribution.contributor, -// contribution.id, -// ) { -// Ok(_) => {}, -// Err(e) => { -// Pallet::::deposit_event(Event::StartContributionVestingScheduleFailed { -// project_id: contribution.project_id, -// contributor: contribution.contributor.clone(), -// id: contribution.id, -// error: e, -// }); -// }, -// } -// ( -// base_weight.saturating_add(WeightInfoOf::::start_contribution_vesting_schedule_for()), -// unscheduled_contributions.count() as u64, -// ) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn mint_ct_for_one_bid(project_id: ProjectId) -> (Weight, u64) { -// let project_bids = Bids::::iter_prefix_values((project_id,)); -// let mut remaining_bids = project_bids -// .filter(|bid| !bid.ct_minted && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(bid) = remaining_bids.next() { -// match Pallet::::do_bid_ct_mint_for( -// &T::PalletId::get().into_account_truncating(), -// bid.project_id, -// &bid.bidder, -// bid.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::CTMintFailed { -// project_id: bid.project_id, -// claimer: bid.bidder.clone(), -// id: bid.id, -// error: e.error, -// }), -// }; -// ( -// base_weight.saturating_add(WeightInfoOf::::bid_ct_mint_for_with_ct_account_creation()), -// remaining_bids.count() as u64, -// ) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn mint_ct_for_one_contribution(project_id: ProjectId) -> (Weight, u64) { -// let project_contributions = Contributions::::iter_prefix_values((project_id,)); -// let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.ct_minted); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(contribution) = remaining_contributions.next() { -// match Pallet::::do_contribution_ct_mint_for( -// &T::PalletId::get().into_account_truncating(), -// contribution.project_id, -// &contribution.contributor, -// contribution.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::CTMintFailed { -// project_id: contribution.project_id, -// claimer: contribution.contributor.clone(), -// id: contribution.id, -// error: e.error, -// }), -// }; -// ( -// base_weight.saturating_add(WeightInfoOf::::contribution_ct_mint_for_with_ct_account_creation()), -// remaining_contributions.count() as u64, -// ) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn issuer_funding_payout_one_bid(project_id: ProjectId) -> (Weight, u64) { -// let project_bids = Bids::::iter_prefix_values((project_id,)); - -// let mut remaining_bids = project_bids.filter(|bid| { -// !bid.funds_released && matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)) -// }); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(bid) = remaining_bids.next() { -// match Pallet::::do_payout_bid_funds_for( -// &T::PalletId::get().into_account_truncating(), -// bid.project_id, -// &bid.bidder, -// bid.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::PayoutContributionFundsFailed { -// project_id: bid.project_id, -// contributor: bid.bidder.clone(), -// id: bid.id, -// error: e, -// }), -// }; -// (base_weight.saturating_add(WeightInfoOf::::payout_bid_funds_for()), remaining_bids.count() as u64) -// } else { -// (base_weight, 0u64) -// } -// } - -// fn issuer_funding_payout_one_contribution(project_id: ProjectId) -> (Weight, u64) { -// let project_contributions = Contributions::::iter_prefix_values((project_id,)); - -// let mut remaining_contributions = project_contributions.filter(|contribution| !contribution.funds_released); -// let base_weight = Weight::from_parts(10_000_000, 0); - -// if let Some(contribution) = remaining_contributions.next() { -// match Pallet::::do_payout_contribution_funds_for( -// &T::PalletId::get().into_account_truncating(), -// contribution.project_id, -// &contribution.contributor, -// contribution.id, -// ) { -// Ok(_) => (), -// Err(e) => Pallet::::deposit_event(Event::PayoutContributionFundsFailed { -// project_id: contribution.project_id, -// contributor: contribution.contributor.clone(), -// id: contribution.id, -// error: e, -// }), -// }; - -// ( -// base_weight.saturating_add(WeightInfoOf::::payout_contribution_funds_for()), -// remaining_contributions.count() as u64, -// ) -// } else { -// (base_weight, 0u64) -// } -// } From bc3c00e89f761f704d27dad6de1d0a86720b20de Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Thu, 21 Mar 2024 16:33:13 +0100 Subject: [PATCH 09/21] remove cleaner --- pallets/funding/src/benchmarking.rs | 4 +-- pallets/funding/src/functions.rs | 4 --- pallets/funding/src/instantiator.rs | 1 - pallets/funding/src/lib.rs | 1 - pallets/funding/src/types.rs | 50 ----------------------------- 5 files changed, 2 insertions(+), 58 deletions(-) diff --git a/pallets/funding/src/benchmarking.rs b/pallets/funding/src/benchmarking.rs index 77dd587c1..09da3ca49 100644 --- a/pallets/funding/src/benchmarking.rs +++ b/pallets/funding/src/benchmarking.rs @@ -2531,7 +2531,7 @@ mod benchmarks { // * validity checks * let project_details = inst.get_project_details(project_id); - assert_eq!(project_details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); + assert_eq!(project_details.status, ProjectStatus::FundingSuccessful); } #[benchmark] @@ -2579,7 +2579,7 @@ mod benchmarks { // * validity checks * let project_details = inst.get_project_details(project_id); - assert_eq!(project_details.cleanup, Cleaner::Failure(CleanerState::Initialized(PhantomData))); + assert_eq!(project_details.status, ProjectStatus::FundingFailed); } #[cfg(test)] diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index f94d6064a..f560d92b6 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -102,7 +102,6 @@ impl Pallet { phase_transition_points: PhaseTransitionPoints::new(now), remaining_contribution_tokens: initial_metadata.total_allocation_size, funding_amount_reached: BalanceOf::::zero(), - cleanup: Cleaner::NotReady, evaluation_round_info: EvaluationRoundInfoOf:: { total_bonded_usd: Zero::zero(), total_bonded_plmc: Zero::zero(), @@ -303,7 +302,6 @@ impl Pallet { } else { // * Update storage * project_details.status = ProjectStatus::EvaluationFailed; - project_details.cleanup = Cleaner::Failure(CleanerState::Initialized(PhantomData::)); ProjectsDetails::::insert(project_id, project_details); // * Emit events * @@ -819,8 +817,6 @@ impl Pallet { ); // * Calculate new variables * - project_details.cleanup = - Cleaner::try_from(project_details.status.clone()).map_err(|_| Error::::NotAllowed)?; project_details.funding_end_block = Some(now); // * Update storage * diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index 6439ccb03..a539ff3ea 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -365,7 +365,6 @@ impl< .unwrap(), remaining_contribution_tokens: expected_metadata.total_allocation_size, funding_amount_reached: BalanceOf::::zero(), - cleanup: Cleaner::NotReady, evaluation_round_info: EvaluationRoundInfoOf:: { total_bonded_usd: Zero::zero(), total_bonded_plmc: Zero::zero(), diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 85a928d66..e96649076 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -150,7 +150,6 @@ pub mod tests; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; -pub mod impls; #[cfg(any(feature = "runtime-benchmarks", feature = "std"))] pub mod instantiator; pub mod traits; diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index 4c2c24e49..4043683dc 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -262,7 +262,6 @@ pub mod storage_types { /// Funding reached amount in USD equivalent pub funding_amount_reached: Balance, /// Cleanup operations remaining - pub cleanup: Cleaner, /// Information about the total amount bonded, and the outcome in regards to reward/slash/nothing pub evaluation_round_info: EvaluationRoundInfo, /// When the Funding Round ends @@ -716,55 +715,6 @@ pub mod inner_types { Unknown, } - #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub struct Success; - - #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub struct Failure; - - #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub enum CleanerState { - Initialized(PhantomData), - // Success or Failure - EvaluationRewardOrSlash(u64, PhantomData), - EvaluationUnbonding(u64, PhantomData), - // Branch - // A. Success only - BidCTMint(u64, PhantomData), - ContributionCTMint(u64, PhantomData), - StartBidderVestingSchedule(u64, PhantomData), - StartContributorVestingSchedule(u64, PhantomData), - BidFundingPayout(u64, PhantomData), - ContributionFundingPayout(u64, PhantomData), - // B. Failure only - BidFundingRelease(u64, PhantomData), - BidUnbonding(u64, PhantomData), - ContributionFundingRelease(u64, PhantomData), - ContributionUnbonding(u64, PhantomData), - // Merge - // Success or Failure - Finished(PhantomData), - } - - #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub enum Cleaner { - NotReady, - Success(CleanerState), - Failure(CleanerState), - } - impl TryFrom for Cleaner { - type Error = (); - - fn try_from(value: ProjectStatus) -> Result { - match value { - ProjectStatus::FundingSuccessful => Ok(Cleaner::Success(CleanerState::Initialized(PhantomData))), - ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => - Ok(Cleaner::Failure(CleanerState::Initialized(PhantomData))), - _ => Err(()), - } - } - } - #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct EvaluationRoundInfo { pub total_bonded_usd: Balance, From d0d104d790497b7562d301d180aed56b679db740 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Thu, 21 Mar 2024 16:42:59 +0100 Subject: [PATCH 10/21] remove unnecessary fields --- pallets/funding/src/benchmarking.rs | 5 -- pallets/funding/src/functions.rs | 7 --- pallets/funding/src/instantiator.rs | 15 ------ pallets/funding/src/lib.rs | 5 +- pallets/funding/src/types.rs | 74 ++--------------------------- 5 files changed, 6 insertions(+), 100 deletions(-) diff --git a/pallets/funding/src/benchmarking.rs b/pallets/funding/src/benchmarking.rs index 09da3ca49..70e4003f1 100644 --- a/pallets/funding/src/benchmarking.rs +++ b/pallets/funding/src/benchmarking.rs @@ -632,13 +632,11 @@ mod benchmarks { evaluator, original_plmc_bond, current_plmc_bond, - rewarded_or_slashed, .. } if project_id == project_id && evaluator == evaluation.account.clone() && original_plmc_bond == extrinsic_plmc_bonded && current_plmc_bond == extrinsic_plmc_bonded && - rewarded_or_slashed.is_none() => {}, _ => assert!(false, "Evaluation is not stored correctly"), } @@ -921,10 +919,7 @@ mod benchmarks { funding_asset_amount_locked: None, multiplier: Some(bid_params.multiplier), plmc_bond: None, - plmc_vesting_info: Some(None), when: None, - funds_released: Some(false), - ct_minted: Some(false), }; Bids::::iter_prefix_values((project_id, bidder.clone())) .find(|stored_bid| bid_filter.matches_bid(stored_bid)) diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index f560d92b6..9f33938fa 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -960,7 +960,6 @@ impl Pallet { early_usd_amount, late_usd_amount, when: now, - rewarded_or_slashed: None, ct_migration_status: MigrationStatus::NotStarted, }; @@ -1170,10 +1169,7 @@ impl Pallet { funding_asset_amount_locked, multiplier, plmc_bond, - plmc_vesting_info: None, when: now, - funds_released: false, - ct_minted: false, ct_migration_status: MigrationStatus::NotStarted, }; @@ -1333,9 +1329,6 @@ impl Pallet { funding_asset, funding_asset_amount, plmc_bond, - plmc_vesting_info: None, - funds_released: false, - ct_minted: false, ct_migration_status: MigrationStatus::NotStarted, }; diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index a539ff3ea..6c3a68ade 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -2931,10 +2931,7 @@ pub struct BidInfoFilter { pub funding_asset_amount_locked: Option>, pub multiplier: Option>, pub plmc_bond: Option>, - pub plmc_vesting_info: Option>>, pub when: Option>, - pub funds_released: Option, - pub ct_minted: Option, } impl BidInfoFilter { pub(crate) fn matches_bid(&self, bid: &BidInfoOf) -> bool { @@ -2976,18 +2973,9 @@ impl BidInfoFilter { if self.plmc_bond.is_some() && self.plmc_bond.unwrap() != bid.plmc_bond { return false; } - if self.plmc_vesting_info.is_some() && self.plmc_vesting_info.unwrap() != bid.plmc_vesting_info { - return false; - } if self.when.is_some() && self.when.unwrap() != bid.when { return false; } - if self.funds_released.is_some() && self.funds_released.unwrap() != bid.funds_released { - return false; - } - if self.ct_minted.is_some() && self.ct_minted.unwrap() != bid.ct_minted { - return false; - } true } @@ -3007,10 +2995,7 @@ impl Default for BidInfoFilter { funding_asset_amount_locked: None, multiplier: None, plmc_bond: None, - plmc_vesting_info: None, when: None, - funds_released: None, - ct_minted: None, } } } diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index e96649076..e599d27b5 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -173,12 +173,11 @@ pub type ProjectMetadataOf = pub type ProjectDetailsOf = ProjectDetails, Did, BlockNumberFor, PriceOf, BalanceOf, EvaluationRoundInfoOf>; pub type EvaluationRoundInfoOf = EvaluationRoundInfo>; -pub type VestingInfoOf = VestingInfo, BalanceOf>; pub type EvaluationInfoOf = EvaluationInfo, BalanceOf, BlockNumberFor>; pub type BidInfoOf = - BidInfo, PriceOf, AccountIdOf, BlockNumberFor, MultiplierOf, VestingInfoOf>; + BidInfo, PriceOf, AccountIdOf, BlockNumberFor, MultiplierOf>; pub type ContributionInfoOf = - ContributionInfo, BalanceOf, MultiplierOf, VestingInfoOf>; + ContributionInfo, BalanceOf, MultiplierOf>; pub type ProjectMigrationOriginsOf = ProjectMigrationOrigins>>; diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index 4043683dc..b7852dd57 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -298,7 +298,6 @@ pub mod storage_types { pub late_usd_amount: Balance, pub when: BlockNumber, // Will be Some after a reward of slash was made on this evaluation. - pub rewarded_or_slashed: Option>, pub ct_migration_status: MigrationStatus, } @@ -310,7 +309,6 @@ pub mod storage_types { AccountId, BlockNumber, Multiplier, - VestingInfo, > { pub id: u32, pub project_id: ProjectId, @@ -325,10 +323,7 @@ pub mod storage_types { pub funding_asset_amount_locked: Balance, pub multiplier: Multiplier, pub plmc_bond: Balance, - pub plmc_vesting_info: Option, pub when: BlockNumber, - pub funds_released: bool, - pub ct_minted: bool, pub ct_migration_status: MigrationStatus, } @@ -339,8 +334,7 @@ pub mod storage_types { AccountId: Eq, BlockNumber: Eq + Ord, Multiplier: Eq, - VestingInfo: Eq, - > Ord for BidInfo + > Ord for BidInfo { fn cmp(&self, other: &Self) -> sp_std::cmp::Ordering { match self.original_ct_usd_price.cmp(&other.original_ct_usd_price) { @@ -357,8 +351,7 @@ pub mod storage_types { AccountId: Eq, BlockNumber: Eq + Ord, Multiplier: Eq, - VestingInfo: Eq, - > PartialOrd for BidInfo + > PartialOrd for BidInfo { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -366,7 +359,7 @@ pub mod storage_types { } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] - pub struct ContributionInfo { + pub struct ContributionInfo { pub id: Id, pub project_id: ProjectId, pub contributor: AccountId, @@ -376,9 +369,6 @@ pub mod storage_types { pub funding_asset: AcceptedFundingAsset, pub funding_asset_amount: Balance, pub plmc_bond: Balance, - pub plmc_vesting_info: Option, - pub funds_released: bool, - pub ct_minted: bool, pub ct_migration_status: MigrationStatus, } @@ -806,60 +796,4 @@ pub mod inner_types { crate::Pallet::::migrations_per_xcm_message_allowed() } } -} - -pub struct MigrationGenerator(PhantomData); - -impl MigrationGenerator { - pub fn evaluation_migration(evaluation: EvaluationInfoOf) -> Option { - if matches!(evaluation.ct_migration_status, MigrationStatus::Confirmed | MigrationStatus::Sent(_)) { - return None; - } - if let Some(RewardOrSlash::Reward(ct_amount)) = evaluation.rewarded_or_slashed { - let multiplier = MultiplierOf::::try_from(1u8).ok()?; - let vesting_duration = multiplier.calculate_vesting_duration::(); - let vesting_duration_local_type = ::BlockNumber::from(vesting_duration); - let migration_origin = MigrationOrigin { - user: T::AccountId32Conversion::convert(evaluation.evaluator), - id: evaluation.id, - participation_type: ParticipationType::Evaluation, - }; - let migration_info: MigrationInfo = (ct_amount.into(), vesting_duration_local_type.into()).into(); - Some(Migration::new(migration_origin, migration_info)) - } else { - None - } - } - - pub fn bid_migration(bid: BidInfoOf) -> Option { - if bid.final_ct_amount == Zero::zero() || - matches!(bid.ct_migration_status, MigrationStatus::Confirmed | MigrationStatus::Sent(_)) - { - return None; - } - let vesting_duration = bid.multiplier.calculate_vesting_duration::(); - let vesting_duration_local_type = ::BlockNumber::from(vesting_duration); - let migration_origin = MigrationOrigin { - user: ::AccountId32Conversion::convert(bid.bidder), - id: bid.id, - participation_type: ParticipationType::Bid, - }; - let migration_info: MigrationInfo = (bid.final_ct_amount.into(), vesting_duration_local_type.into()).into(); - Some(Migration::new(migration_origin, migration_info)) - } - - pub fn contribution_migration(contribution: ContributionInfoOf) -> Option { - if matches!(contribution.ct_migration_status, MigrationStatus::Confirmed | MigrationStatus::Sent(_)) { - return None; - } - let vesting_duration = contribution.multiplier.calculate_vesting_duration::(); - let vesting_duration_local_type = ::BlockNumber::from(vesting_duration); - let migration_origin = MigrationOrigin { - user: ::AccountId32Conversion::convert(contribution.contributor), - id: contribution.id, - participation_type: ParticipationType::Contribution, - }; - let migration_info: MigrationInfo = (contribution.ct_amount.into(), vesting_duration_local_type.into()).into(); - Some(Migration::new(migration_origin, migration_info)) - } -} +} \ No newline at end of file From 24dc844100cf85f399e1cb338ceeec2fa568a670 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Fri, 22 Mar 2024 09:48:08 +0100 Subject: [PATCH 11/21] update migrations + update events --- Cargo.lock | 1 + integration-tests/src/tests/ct_migration.rs | 4 +- pallets/funding/src/functions.rs | 177 ++++++++++---------- pallets/funding/src/lib.rs | 168 ++----------------- pallets/funding/src/settlement.rs | 60 +++++-- pallets/funding/src/tests.rs | 5 + pallets/funding/src/types.rs | 12 +- pallets/polimec-receiver/src/lib.rs | 1 + polimec-common/common/Cargo.toml | 4 + polimec-common/common/src/lib.rs | 26 +-- 10 files changed, 169 insertions(+), 289 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7555f535..2430dfb8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7556,6 +7556,7 @@ dependencies = [ "serde", "sp-runtime", "sp-std", + "xcm", ] [[package]] diff --git a/integration-tests/src/tests/ct_migration.rs b/integration-tests/src/tests/ct_migration.rs index b4834b4da..34f9d5973 100644 --- a/integration-tests/src/tests/ct_migration.rs +++ b/integration-tests/src/tests/ct_migration.rs @@ -17,11 +17,11 @@ use crate::*; use pallet_funding::{ assert_close_enough, traits::VestingDurationCalculation, AcceptedFundingAsset, BidStatus, EvaluatorsOutcome, - MigrationStatus, Multiplier, MultiplierOf, ProjectId, RewardOrSlash, + Multiplier, MultiplierOf, ProjectId, RewardOrSlash, }; use polimec_common::{ credentials::InvestorType, - migration_types::{Migration, MigrationInfo, MigrationOrigin, Migrations, ParticipationType}, + migration_types::{Migration, MigrationInfo, MigrationOrigin, Migrations, MigrationStatus, ParticipationType}, }; use polimec_parachain_runtime::PolimecFunding; use sp_runtime::{traits::Convert, FixedPointNumber, Perquintill}; diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index 9f33938fa..a4e4b391f 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -960,7 +960,6 @@ impl Pallet { early_usd_amount, late_usd_amount, when: now, - ct_migration_status: MigrationStatus::NotStarted, }; if caller_existing_evaluations.len() < T::MaxEvaluationsPerUser::get() as usize { @@ -1170,7 +1169,6 @@ impl Pallet { multiplier, plmc_bond, when: now, - ct_migration_status: MigrationStatus::NotStarted, }; Self::try_plmc_participation_lock(bidder, project_id, plmc_bond)?; @@ -1329,7 +1327,6 @@ impl Pallet { funding_asset, funding_asset_amount, plmc_bond, - ct_migration_status: MigrationStatus::NotStarted, }; // Try adding the new contribution to the system @@ -2420,35 +2417,35 @@ impl Pallet { let project_id = project_migration_origins.project_id; let migration_origins = project_migration_origins.migration_origins; for MigrationOrigin { user, id, participation_type } in migration_origins { - match participation_type { - ParticipationType::Evaluation => { - Evaluations::::mutate( - (project_id, T::AccountId32Conversion::convert_back(user), id), - |maybe_evaluation| { - if let Some(evaluation) = maybe_evaluation { - evaluation.ct_migration_status = MigrationStatus::Sent(query_id); - } - }, - ); - }, - ParticipationType::Bid => { - Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { - if let Some(bid) = maybe_bid { - bid.ct_migration_status = MigrationStatus::Sent(query_id); - } - }); - }, - ParticipationType::Contribution => { - Contributions::::mutate( - (project_id, T::AccountId32Conversion::convert_back(user), id), - |maybe_contribution| { - if let Some(contribution) = maybe_contribution { - contribution.ct_migration_status = MigrationStatus::Sent(query_id); - } - }, - ); - }, - } + // match participation_type { + // ParticipationType::Evaluation => { + // Evaluations::::mutate( + // (project_id, T::AccountId32Conversion::convert_back(user), id), + // |maybe_evaluation| { + // if let Some(evaluation) = maybe_evaluation { + // evaluation.ct_migration_status = MigrationStatus::Sent(query_id); + // } + // }, + // ); + // }, + // ParticipationType::Bid => { + // Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { + // if let Some(bid) = maybe_bid { + // bid.ct_migration_status = MigrationStatus::Sent(query_id); + // } + // }); + // }, + // ParticipationType::Contribution => { + // Contributions::::mutate( + // (project_id, T::AccountId32Conversion::convert_back(user), id), + // |maybe_contribution| { + // if let Some(contribution) = maybe_contribution { + // contribution.ct_migration_status = MigrationStatus::Sent(query_id); + // } + // }, + // ); + // }, + // } } } @@ -2456,35 +2453,35 @@ impl Pallet { let project_id = project_migration_origins.project_id; let migration_origins = project_migration_origins.migration_origins; for MigrationOrigin { user, id, participation_type } in migration_origins { - match participation_type { - ParticipationType::Evaluation => { - Evaluations::::mutate( - (project_id, T::AccountId32Conversion::convert_back(user), id), - |maybe_evaluation| { - if let Some(evaluation) = maybe_evaluation { - evaluation.ct_migration_status = MigrationStatus::Confirmed; - } - }, - ); - }, - ParticipationType::Bid => { - Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { - if let Some(bid) = maybe_bid { - bid.ct_migration_status = MigrationStatus::Confirmed; - } - }); - }, - ParticipationType::Contribution => { - Contributions::::mutate( - (project_id, T::AccountId32Conversion::convert_back(user), id), - |maybe_contribution| { - if let Some(contribution) = maybe_contribution { - contribution.ct_migration_status = MigrationStatus::Confirmed; - } - }, - ); - }, - } + // match participation_type { + // ParticipationType::Evaluation => { + // Evaluations::::mutate( + // (project_id, T::AccountId32Conversion::convert_back(user), id), + // |maybe_evaluation| { + // if let Some(evaluation) = maybe_evaluation { + // evaluation.ct_migration_status = MigrationStatus::Confirmed; + // } + // }, + // ); + // }, + // ParticipationType::Bid => { + // Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { + // if let Some(bid) = maybe_bid { + // bid.ct_migration_status = MigrationStatus::Confirmed; + // } + // }); + // }, + // ParticipationType::Contribution => { + // Contributions::::mutate( + // (project_id, T::AccountId32Conversion::convert_back(user), id), + // |maybe_contribution| { + // if let Some(contribution) = maybe_contribution { + // contribution.ct_migration_status = MigrationStatus::Confirmed; + // } + // }, + // ); + // }, + // } } } @@ -2495,35 +2492,35 @@ impl Pallet { let project_id = project_migration_origins.project_id; let migration_origins = project_migration_origins.migration_origins; for MigrationOrigin { user, id, participation_type } in migration_origins { - match participation_type { - ParticipationType::Evaluation => { - Evaluations::::mutate( - (project_id, T::AccountId32Conversion::convert_back(user), id), - |maybe_evaluation| { - if let Some(evaluation) = maybe_evaluation { - evaluation.ct_migration_status = MigrationStatus::Failed(error.clone()); - } - }, - ); - }, - ParticipationType::Bid => { - Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { - if let Some(bid) = maybe_bid { - bid.ct_migration_status = MigrationStatus::Failed(error.clone()); - } - }); - }, - ParticipationType::Contribution => { - Contributions::::mutate( - (project_id, T::AccountId32Conversion::convert_back(user), id), - |maybe_contribution| { - if let Some(contribution) = maybe_contribution { - contribution.ct_migration_status = MigrationStatus::Failed(error.clone()); - } - }, - ); - }, - } + // match participation_type { + // ParticipationType::Evaluation => { + // Evaluations::::mutate( + // (project_id, T::AccountId32Conversion::convert_back(user), id), + // |maybe_evaluation| { + // if let Some(evaluation) = maybe_evaluation { + // evaluation.ct_migration_status = MigrationStatus::Failed(error.clone()); + // } + // }, + // ); + // }, + // ParticipationType::Bid => { + // Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { + // if let Some(bid) = maybe_bid { + // bid.ct_migration_status = MigrationStatus::Failed(error.clone()); + // } + // }); + // }, + // ParticipationType::Contribution => { + // Contributions::::mutate( + // (project_id, T::AccountId32Conversion::convert_back(user), id), + // |maybe_contribution| { + // if let Some(contribution) = maybe_contribution { + // contribution.ct_migration_status = MigrationStatus::Failed(error.clone()); + // } + // }, + // ); + // }, + // } } } } diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index e599d27b5..b48dc039a 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -616,13 +616,6 @@ use sp_arithmetic::Percent; amount: BalanceOf, bonder: AccountIdOf, }, - /// Someone paid for the release of a user's PLMC bond for a project. - BondReleased { - project_id: ProjectId, - amount: BalanceOf, - bonder: AccountIdOf, - releaser: AccountIdOf, - }, /// A bid was made for a project Bid { project_id: ProjectId, @@ -655,155 +648,28 @@ use sp_arithmetic::Percent; project_id: ProjectId, error: DispatchError, }, - /// Something terribly wrong happened where the bond could not be unbonded. Most likely a programming error - EvaluationUnbondFailed { - project_id: ProjectId, - evaluator: AccountIdOf, - id: u32, - error: DispatchError, - }, - /// Contribution tokens were minted to a user - ContributionTokenMinted { - releaser: AccountIdOf, - project_id: ProjectId, - claimer: AccountIdOf, - amount: BalanceOf, - }, - /// A transfer of tokens failed, but because it was done inside on_initialize it cannot be solved. - TransferError { - error: DispatchError, - }, - EvaluationRewardFailed { - project_id: ProjectId, - evaluator: AccountIdOf, - id: u32, - error: DispatchError, - }, - EvaluationSlashFailed { - project_id: ProjectId, - evaluator: AccountIdOf, - id: u32, - error: DispatchError, - }, - ReleaseBidFundsFailed { - project_id: ProjectId, - bidder: AccountIdOf, - id: u32, - error: DispatchError, - }, - BidUnbondFailed { - project_id: ProjectId, - bidder: AccountIdOf, - id: u32, - error: DispatchError, - }, - ReleaseContributionFundsFailed { - project_id: ProjectId, - contributor: AccountIdOf, - id: u32, - error: DispatchError, - }, - ContributionUnbondFailed { - project_id: ProjectId, - contributor: AccountIdOf, - id: u32, - error: DispatchError, - }, - PayoutContributionFundsFailed { - project_id: ProjectId, - contributor: AccountIdOf, - id: u32, - error: DispatchError, - }, - PayoutBidFundsFailed { - project_id: ProjectId, - bidder: AccountIdOf, - id: u32, - error: DispatchError, - }, - EvaluationRewarded { - project_id: ProjectId, - evaluator: AccountIdOf, - id: u32, - amount: BalanceOf, - caller: AccountIdOf, - }, - EvaluationSlashed { - project_id: ProjectId, - evaluator: AccountIdOf, - id: u32, - amount: BalanceOf, - caller: AccountIdOf, - }, - CTMintFailed { - project_id: ProjectId, - claimer: AccountIdOf, - id: u32, - error: DispatchError, - }, - StartBidderVestingScheduleFailed { - project_id: ProjectId, - bidder: AccountIdOf, - id: u32, - error: DispatchError, - }, - StartContributionVestingScheduleFailed { - project_id: ProjectId, - contributor: AccountIdOf, - id: u32, - error: DispatchError, - }, - BidPlmcVestingScheduled { - project_id: ProjectId, - bidder: AccountIdOf, - id: u32, - amount: BalanceOf, - caller: AccountIdOf, - }, - ContributionPlmcVestingScheduled { - project_id: ProjectId, - contributor: AccountIdOf, - id: u32, - amount: BalanceOf, - caller: AccountIdOf, - }, - ParticipantPlmcVested { - project_id: ProjectId, - participant: AccountIdOf, - amount: BalanceOf, - caller: AccountIdOf, - }, - BidFundingPaidOut { + ProjectOutcomeDecided { project_id: ProjectId, - bidder: AccountIdOf, - id: u32, - amount: BalanceOf, - caller: AccountIdOf, + decision: FundingOutcomeDecision, }, - ContributionFundingPaidOut { + EvaluationSettled { project_id: ProjectId, - contributor: AccountIdOf, + account: AccountIdOf, id: u32, - amount: BalanceOf, - caller: AccountIdOf, + ct_amount: BalanceOf, + slashed_amount: BalanceOf, }, - BidFundingReleased { + BidSettled { project_id: ProjectId, - bidder: AccountIdOf, + account: AccountIdOf, id: u32, - amount: BalanceOf, - caller: AccountIdOf, + ct_amount: BalanceOf, }, - ContributionFundingReleased { + ContributionSettled { project_id: ProjectId, - contributor: AccountIdOf, + account: AccountIdOf, id: u32, - amount: BalanceOf, - caller: AccountIdOf, - }, - ProjectOutcomeDecided { - project_id: ProjectId, - decision: FundingOutcomeDecision, + ct_amount: BalanceOf, }, ProjectParaIdSet { project_id: ProjectId, @@ -851,16 +717,6 @@ use sp_arithmetic::Percent; project_id: ProjectId, migration_origins: BoundedVec>, }, - ReleaseFutureCTDepositFailed { - project_id: ProjectId, - participant: AccountIdOf, - error: DispatchError, - }, - FutureCTDepositReleased { - project_id: ProjectId, - participant: AccountIdOf, - caller: AccountIdOf, - }, } #[pallet::error] diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index 719561457..5f5fc5226 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -15,7 +15,7 @@ use frame_support::{ }; use sp_runtime::{Perquintill, traits::{Zero, Convert}}; use polimec_common::{ - migration_types::{MigrationInfo, MigrationOrigin, ParticipationType}, + migration_types::{MigrationInfo, MigrationOrigin, MigrationStatus, ParticipationType}, ReleaseSchedule, }; @@ -48,9 +48,15 @@ impl Pallet { let duration = multiplier.calculate_vesting_duration::(); Self::create_migration(project_id, &evaluation.evaluator, evaluation.id, ParticipationType::Evaluation, reward, duration)?; } - Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); + Evaluations::::remove((project_id, evaluation.evaluator.clone(), evaluation.id)); - // TODO: Emit an event + Self::deposit_event(Event::EvaluationSettled { + project_id, + account: evaluation.evaluator, + id: evaluation.id, + ct_amount: reward, + slashed_amount: evaluation.current_plmc_bond.saturating_sub(bond), + }); Ok(()) } @@ -78,9 +84,15 @@ impl Pallet { Precision::Exact, )?; - Evaluations::::remove((project_id, evaluation.evaluator, evaluation.id)); + Evaluations::::remove((project_id, evaluation.evaluator.clone(), evaluation.id)); - // TODO: Emit an event + Self::deposit_event(Event::EvaluationSettled { + project_id, + account: evaluation.evaluator, + id: evaluation.id, + ct_amount: Zero::zero(), + slashed_amount: evaluation.current_plmc_bond.saturating_sub(bond), + }); Ok(()) } @@ -120,9 +132,14 @@ impl Pallet { Self::create_migration(project_id, &bidder, bid.id, ParticipationType::Bid, bid.final_ct_amount, vest_info.duration.into())?; // TODO: Create MigrationInfo - Bids::::remove((project_id, bidder, bid.id)); + Bids::::remove((project_id, bidder.clone(), bid.id)); - // TODO: Emit an event + Self::deposit_event(Event::BidSettled { + project_id, + account: bidder, + id: bid.id, + ct_amount: bid.final_ct_amount, + }); Ok(()) } @@ -146,9 +163,14 @@ impl Pallet { } // Remove the bid from the storage - Bids::::remove((project_id, bidder, bid.id)); + Bids::::remove((project_id, bidder.clone(), bid.id)); - // TODO: Emit an event + Self::deposit_event(Event::BidSettled { + project_id, + account: bidder, + id: bid.id, + ct_amount: Zero::zero(), + }); Ok(()) } @@ -185,7 +207,14 @@ impl Pallet { // Create Migration Self::create_migration(project_id, &contributor, contribution.id, ParticipationType::Contribution, contribution.ct_amount, vest_info.duration.into())?; - Contributions::::remove((project_id, contributor, contribution.id)); + Contributions::::remove((project_id, contributor.clone(), contribution.id)); + + Self::deposit_event(Event::ContributionSettled { + project_id, + account: contributor, + id: contribution.id, + ct_amount: contribution.ct_amount, + }); Ok(()) } @@ -209,9 +238,14 @@ impl Pallet { // Remove the bid from the storage - Contributions::::remove((project_id, contributor, contribution.id)); - - // TODO: Emit an event + Contributions::::remove((project_id, contributor.clone(), contribution.id)); + + Self::deposit_event(Event::ContributionSettled { + project_id, + account: contributor, + id: contribution.id, + ct_amount: Zero::zero(), + }); Ok(()) } diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index 5b4bee08e..cb0c37f0d 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -5642,6 +5642,11 @@ mod ct_migration { use super::*; use frame_support::assert_err; + #[test] + fn max_number_of_migrations() { + dbg!(Pallet::::migrations_per_xcm_message_allowed()); + } + #[test] fn para_id_for_project_can_be_set_by_issuer() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index b7852dd57..ef411a2f2 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -297,8 +297,6 @@ pub mod storage_types { pub early_usd_amount: Balance, pub late_usd_amount: Balance, pub when: BlockNumber, - // Will be Some after a reward of slash was made on this evaluation. - pub ct_migration_status: MigrationStatus, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] @@ -324,7 +322,6 @@ pub mod storage_types { pub multiplier: Multiplier, pub plmc_bond: Balance, pub when: BlockNumber, - pub ct_migration_status: MigrationStatus, } impl< @@ -369,7 +366,6 @@ pub mod storage_types { pub funding_asset: AcceptedFundingAsset, pub funding_asset_amount: Balance, pub plmc_bond: Balance, - pub ct_migration_status: MigrationStatus, } /// Represents a bucket that holds a specific amount of tokens at a given price. @@ -781,13 +777,7 @@ pub mod inner_types { pub project_id: ProjectId, pub migration_origins: MigrationOrigins, } - #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub enum MigrationStatus { - NotStarted, - Sent(xcm::v3::QueryId), - Confirmed, - Failed(BoundedVec), - } + #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct MaxMigrationsPerXcm(PhantomData); diff --git a/pallets/polimec-receiver/src/lib.rs b/pallets/polimec-receiver/src/lib.rs index e41692fc0..1388b2a6d 100644 --- a/pallets/polimec-receiver/src/lib.rs +++ b/pallets/polimec-receiver/src/lib.rs @@ -107,6 +107,7 @@ pub mod pallet { for migration @ Migration { origin: MigrationOrigin { user, id, participation_type }, info: MigrationInfo { contribution_token_amount, .. }, + .. } in migrations.clone().inner() { let already_executed = ExecutedMigrations::::get((user, participation_type, id)); diff --git a/polimec-common/common/Cargo.toml b/polimec-common/common/Cargo.toml index 09ab71762..08959c4cc 100644 --- a/polimec-common/common/Cargo.toml +++ b/polimec-common/common/Cargo.toml @@ -24,6 +24,7 @@ pallet-timestamp.workspace = true sp-std.workspace = true sp-runtime.workspace = true itertools.workspace = true +xcm.workspace = true [features] @@ -41,6 +42,7 @@ std = [ "serde/std", "sp-runtime/std", "sp-std/std", + "xcm/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -48,10 +50,12 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "xcm/std", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-timestamp/try-runtime", "sp-runtime/try-runtime", + "xcm/std", ] diff --git a/polimec-common/common/src/lib.rs b/polimec-common/common/src/lib.rs index a3e5e7d20..997b1a28f 100644 --- a/polimec-common/common/src/lib.rs +++ b/polimec-common/common/src/lib.rs @@ -141,11 +141,20 @@ pub mod migration_types { } } + #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen)] + pub enum MigrationStatus { + NotStarted, + Sent(xcm::v3::QueryId), + Confirmed, + Failed(BoundedVec), + } + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct Migration { pub origin: MigrationOrigin, pub info: MigrationInfo, } + impl Migration { pub fn new(origin: MigrationOrigin, info: MigrationInfo) -> Self { Self { origin, info } @@ -185,23 +194,6 @@ pub mod migration_types { self.0.iter().map(|migration| migration.info.clone()).collect() } - pub fn group_by_user(self) -> Vec<([u8; 32], Vec)> { - let mut migrations = self.0; - migrations.sort_by(|a, b| a.origin.user.cmp(&b.origin.user)); - migrations - .into_iter() - .group_by(|migration| migration.origin.user) - .into_iter() - .map(|(user, migrations)| (user, migrations.collect::>())) - .collect::>() - } - - pub fn sort_by_ct_amount(self) -> Migrations { - let mut migrations = self.0; - migrations.sort_by(|a, b| a.info.contribution_token_amount.cmp(&b.info.contribution_token_amount)); - Migrations(migrations) - } - pub fn total_ct_amount(&self) -> u128 { self.0.iter().map(|migration| migration.info.contribution_token_amount).sum() } From b282b9f8a738e9ccffd2af1e1c8414b1b4608ba9 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Fri, 22 Mar 2024 15:02:09 +0100 Subject: [PATCH 12/21] update migration logic --- pallets/funding/src/benchmarking.rs | 177 ++++++++++------- pallets/funding/src/functions.rs | 292 +++++++--------------------- pallets/funding/src/lib.rs | 50 ++--- pallets/funding/src/settlement.rs | 11 +- pallets/funding/src/tests.rs | 2 - pallets/funding/src/types.rs | 9 +- polimec-common/common/src/lib.rs | 3 +- 7 files changed, 201 insertions(+), 343 deletions(-) diff --git a/pallets/funding/src/benchmarking.rs b/pallets/funding/src/benchmarking.rs index 70e4003f1..8de5a305f 100644 --- a/pallets/funding/src/benchmarking.rs +++ b/pallets/funding/src/benchmarking.rs @@ -626,7 +626,7 @@ mod benchmarks { .last() .unwrap(); - match stored_evaluation { + let correct = match stored_evaluation { EvaluationInfo { project_id, evaluator, @@ -636,9 +636,10 @@ mod benchmarks { } if project_id == project_id && evaluator == evaluation.account.clone() && original_plmc_bond == extrinsic_plmc_bonded && - current_plmc_bond == extrinsic_plmc_bonded && - _ => assert!(false, "Evaluation is not stored correctly"), - } + current_plmc_bond == extrinsic_plmc_bonded => true, + _ => false, + }; + assert!(correct, "Evaluation is not stored correctly"); // Balances let bonded_plmc = inst.get_reserved_plmc_balances_for( @@ -1456,16 +1457,16 @@ mod benchmarks { assert_eq!(ct_amount, reward); // Events - // frame_system::Pallet::::assert_last_event( - // Event::EvaluationRewarded { - // project_id, - // evaluator: evaluator.clone(), - // id: stored_evaluation.id, - // amount: total_reward, - // caller: evaluator, - // } - // .into(), - // ); + frame_system::Pallet::::assert_last_event( + Event::EvaluationSettled { + project_id, + account: evaluator.clone(), + id: evaluation_to_settle.id, + ct_amount: reward, + slashed_amount: 0.into(), + } + .into(), + ); } #[benchmark] @@ -1532,7 +1533,16 @@ mod benchmarks { assert_eq!(free_treasury_plmc, slashed_amount + ed); // Events - // TODO assert Event + frame_system::Pallet::::assert_last_event( + Event::EvaluationSettled { + project_id, + account: evaluator.clone(), + id: evaluation_to_settle.id, + ct_amount: 0.into(), + slashed_amount: slashed_amount, + } + .into(), + ); } #[benchmark] @@ -1577,61 +1587,17 @@ mod benchmarks { assert_eq!(bid_to_settle.final_ct_amount, ct_amount); // Events - // TODO assert Event - } - - #[benchmark] - fn settle_successful_contribution() { - // setup - let mut inst = BenchInstantiator::::new(None); - - // real benchmark starts at block 0, and we can't call `events()` at block 0 - inst.advance_time(1u32.into()).unwrap(); - - let issuer = account::>("issuer", 0, 0); - let contributions = default_community_contributions::(); - let contributor = contributions[0].contributor.clone(); - whitelist_account!(contributor); - - let project_id = inst.create_finished_project( - default_project::(inst.get_new_nonce(), issuer.clone()), - issuer, - default_evaluations::(), - default_bids::(), - contributions, - vec![], - ); - - run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - - let contribution_to_settle = - inst.execute(|| Contributions::::iter_prefix_values((project_id, contributor.clone())).next().unwrap()); - - #[extrinsic_call] - settle_successful_contribution( - RawOrigin::Signed(contributor.clone()), - project_id, - contributor.clone(), - contribution_to_settle.id, + frame_system::Pallet::::assert_last_event( + Event::BidSettled { + project_id, + account: bidder.clone(), + id: bid_to_settle.id, + ct_amount, + } + .into(), ); - - // * validity checks * - // Storage - assert!(Contributions::::get((project_id, contributor.clone(), contribution_to_settle.id)).is_none()); - - // Balances - let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![contributor.clone()])[0]; - assert_eq!(contribution_to_settle.ct_amount, ct_amount); - - // Events - // TODO assert Event } - - - #[benchmark] fn settle_failed_bid() { // setup @@ -1686,7 +1652,72 @@ mod benchmarks { assert_eq!(free_assets, bid_to_settle.funding_asset_amount_locked + free_assets_before); // Events - // TODO assert Event + frame_system::Pallet::::assert_last_event( + Event::BidSettled { + project_id, + account: bidder.clone(), + id: bid_to_settle.id, + ct_amount: 0.into(), + } + .into(), + ); + } + + #[benchmark] + fn settle_successful_contribution() { + // setup + let mut inst = BenchInstantiator::::new(None); + + // real benchmark starts at block 0, and we can't call `events()` at block 0 + inst.advance_time(1u32.into()).unwrap(); + + let issuer = account::>("issuer", 0, 0); + let contributions = default_community_contributions::(); + let contributor = contributions[0].contributor.clone(); + whitelist_account!(contributor); + + let project_id = inst.create_finished_project( + default_project::(inst.get_new_nonce(), issuer.clone()), + issuer, + default_evaluations::(), + default_bids::(), + contributions, + vec![], + ); + + run_blocks_to_execute_next_transition(project_id, UpdateType::StartSettlement, &mut inst); + + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); + + let contribution_to_settle = + inst.execute(|| Contributions::::iter_prefix_values((project_id, contributor.clone())).next().unwrap()); + + #[extrinsic_call] + settle_successful_contribution( + RawOrigin::Signed(contributor.clone()), + project_id, + contributor.clone(), + contribution_to_settle.id, + ); + + // * validity checks * + // Storage + assert!(Contributions::::get((project_id, contributor.clone(), contribution_to_settle.id)).is_none()); + + // Balances + let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![contributor.clone()])[0]; + assert_eq!(contribution_to_settle.ct_amount, ct_amount); + + // Events + frame_system::Pallet::::assert_last_event( + Event::ContributionSettled { + project_id, + account: contributor.clone(), + id: contribution_to_settle.id, + ct_amount, + } + .into(), + ); } #[benchmark] @@ -1750,7 +1781,15 @@ mod benchmarks { assert_eq!(free_assets, contribution_to_settle.funding_asset_amount + free_assets_before); // Events - // TODO assert Event + frame_system::Pallet::::assert_last_event( + Event::ContributionSettled { + project_id, + account: contributor.clone(), + id: contribution_to_settle.id, + ct_amount: 0.into(), + } + .into(), + ); } //do_evaluation_end diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index a4e4b391f..c8bd7ef0d 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -24,27 +24,24 @@ use frame_support::{ pallet_prelude::*, traits::{ fungible::{Mutate, MutateHold as FungibleMutateHold}, - fungibles::{metadata::Mutate as MetadataMutate, Create, Inspect, Mutate as FungiblesMutate}, - tokens::{Fortitude, Precision, Preservation, Restriction}, + fungibles::{metadata::Mutate as MetadataMutate, Create, Mutate as FungiblesMutate}, + tokens::{Precision, Preservation}, Get, }, }; use frame_system::pallet_prelude::BlockNumberFor; use polimec_common::{ credentials::{Did, InvestorType}, - ReleaseSchedule, }; use sp_arithmetic::{ traits::{CheckedDiv, CheckedSub, Zero}, Percent, Perquintill, }; -use sp_runtime::traits::{Convert, ConvertBack}; -use sp_std::marker::PhantomData; -use xcm::v3::MaxDispatchErrorLen; +use sp_runtime::traits::{Convert}; use super::*; use crate::traits::{BondingRequirementCalculation, ProvideAssetPrice, VestingDurationCalculation}; -use polimec_common::migration_types::{MigrationInfo, MigrationOrigin, Migrations, ParticipationType}; +use polimec_common::migration_types::{MigrationInfo, Migrations}; const POLIMEC_PARA_ID: u32 = 3344u32; const QUERY_RESPONSE_TIME_WINDOW_BLOCKS: u32 = 20u32; @@ -1726,26 +1723,7 @@ impl Pallet { Ok(()) } - pub fn do_start_migration(caller: &AccountIdOf, project_id: ProjectId) -> DispatchResult { - // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let migration_readiness_check = project_details.migration_readiness_check.ok_or(Error::::NotAllowed)?; - - // * Validity Checks * - ensure!(caller.clone() == project_details.issuer_account, Error::::NotAllowed); - - ensure!(migration_readiness_check.is_ready(), Error::::NotAllowed); - - // Start automated migration process - - // * Emit events * - Self::deposit_event(Event::::MigrationStarted { project_id }); - - Ok(()) - } - pub fn do_migrate_one_participant( - caller: AccountIdOf, project_id: ProjectId, participant: AccountIdOf, ) -> DispatchResult { @@ -1754,88 +1732,60 @@ impl Pallet { let migration_readiness_check = project_details.migration_readiness_check.ok_or(Error::::NotAllowed)?; let project_para_id = project_details.parachain_id.ok_or(Error::::ImpossibleState)?; let now = >::block_number(); + let (_, migrations) = UserMigrations::::get(project_id, participant.clone()).ok_or(Error::::NoMigrationsFound)?; // * Validity Checks * ensure!(migration_readiness_check.is_ready(), Error::::NotAllowed); - // * Process Data * - - let migrations = MigrationQueue::::get(project_id, participant.clone()); - ensure!(!migrations.is_empty(), Error::::NoMigrationsFound); - let constructed_migrations = Self::construct_migration_xcm_messages(migrations); - for (migrations, xcm) in constructed_migrations { - let project_multilocation = MultiLocation { parents: 1, interior: X1(Parachain(project_para_id.into())) }; - let project_migration_origins = ProjectMigrationOriginsOf:: { - project_id, - migration_origins: migrations - .origins() - .try_into() - .expect("construct function uses same constraint T::MaxMigrationsPerXcm"), - }; + let project_multilocation = MultiLocation { parents: 1, interior: X1(Parachain(project_para_id.into())) }; + let call: ::RuntimeCall = + Call::confirm_migrations { query_id: Default::default(), response: Default::default() }.into(); + let query_id = + pallet_xcm::Pallet::::new_notify_query(project_multilocation, call.into(), now + 20u32.into(), Here); + + Self::change_migration_status(project_id, participant.clone(), MigrationStatus::Sent(query_id))?; - let call: ::RuntimeCall = - Call::confirm_migrations { query_id: Default::default(), response: Default::default() }.into(); - let transact_response_query_id = - pallet_xcm::Pallet::::new_notify_query(project_multilocation, call.into(), now + 20u32.into(), Here); - // TODO: check these values - let max_weight = Weight::from_parts(700_000_000, 10_000); + // * Process Data * + let xcm = Self::construct_migration_xcm_message(migrations.into(), query_id); - let mut instructions = xcm.into_inner(); - instructions.push(ReportTransactStatus(QueryResponseInfo { - destination: ParentThen(X1(Parachain(POLIMEC_PARA_ID))).into(), - query_id: transact_response_query_id, - max_weight, - })); - let xcm = Xcm(instructions); + >::send_xcm(Here, project_multilocation, xcm).map_err(|_| Error::::XcmFailed)?; + ActiveMigrationQueue::::insert(query_id, (project_id, participant.clone())); - >::send_xcm(Here, project_multilocation, xcm).map_err(|_| Error::::XcmFailed)?; - Self::mark_migrations_as_sent(project_migration_origins.clone(), transact_response_query_id); - UnconfirmedMigrations::::insert(transact_response_query_id, project_migration_origins); + Self::deposit_event(Event::::MigrationStatusUpdated { + project_id, + account: participant, + status: MigrationStatus::Sent(query_id) + }); - Self::deposit_event(Event::::UserMigrationSent { - project_id, - caller: caller.clone(), - participant: participant.clone(), - }); - } Ok(()) } pub fn do_confirm_migrations(location: MultiLocation, query_id: QueryId, response: Response) -> DispatchResult { use xcm::v3::prelude::*; - let unconfirmed_migrations = UnconfirmedMigrations::::take(query_id).ok_or(Error::::NotAllowed)?; - let project_id = unconfirmed_migrations.project_id; - let para_id = if let MultiLocation { parents: 1, interior: X1(Parachain(para_id)) } = location { - ParaId::from(para_id) - } else { - return Err(Error::::NotAllowed.into()); - }; + let (project_id, participant) = ActiveMigrationQueue::::take(query_id)?; let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!(project_details.parachain_id == Some(para_id), Error::::NotAllowed); + ensure!(matches!(location, MultiLocation { parents: 1, interior: X1(Parachain(para_id))} if Some(ParaId::from(para_id)) == project_details.parachain_id), Error::::NotAllowed); + - match response { + let status = match response { Response::DispatchResult(MaybeErrorCode::Success) => { - Self::mark_migrations_as_confirmed(unconfirmed_migrations.clone()); - Self::deposit_event(Event::MigrationsConfirmed { - project_id, - migration_origins: unconfirmed_migrations.migration_origins, - }); - // Self::deposit_event(Event::MigrationsConfirmed { project_id }); - Ok(()) + Self::change_migration_status(project_id, participant.clone(), MigrationStatus::Confirmed)?; + MigrationStatus::Confirmed }, - Response::DispatchResult(MaybeErrorCode::Error(e)) | - Response::DispatchResult(MaybeErrorCode::TruncatedError(e)) => { - Self::mark_migrations_as_failed(unconfirmed_migrations.clone(), e); - Self::deposit_event(Event::MigrationsFailed { - project_id, - migration_origins: unconfirmed_migrations.migration_origins, - }); - // Self::deposit_event(Event::MigrationsFailed { project_id}); - Ok(()) + Response::DispatchResult(MaybeErrorCode::Error(_)) | + Response::DispatchResult(MaybeErrorCode::TruncatedError(_)) => { + Self::change_migration_status(project_id, participant.clone(), MigrationStatus::Failed)?; + MigrationStatus::Failed }, - _ => Err(Error::::NotAllowed.into()), - } + _ => return Err(Error::::NotAllowed.into()), + }; + Self::deposit_event(Event::::MigrationStatusUpdated{ + project_id, + account: participant, + status: status, + }); + Ok(()) } } @@ -2380,147 +2330,41 @@ impl Pallet { available_bytes_for_migration_per_message.saturating_div(one_migration_bytes) } - pub fn construct_migration_xcm_messages(migrations: BoundedVec>) -> Vec<(Migrations, Xcm<()>)> { + pub fn construct_migration_xcm_message(migrations: Vec, query_id: QueryId) -> Xcm<()> { // TODO: adjust this as benchmarks for polimec-receiver are written const MAX_WEIGHT: Weight = Weight::from_parts(10_000, 0); - + const MAX_RESPONSE_WEIGHT: Weight = Weight::from_parts(700_000_000, 10_000); // const MAX_WEIGHT: Weight = Weight::from_parts(100_003_000_000_000, 10_000_196_608); let _polimec_receiver_info = T::PolimecReceiverInfo::get(); - // TODO: use the actual pallet index when the fields are not private anymore (https://github.com/paritytech/polkadot-sdk/pull/2231) - let mut output = Vec::new(); - - for migrations_slice in migrations.chunks(MaxMigrationsPerXcm::::get() as usize) { - let migrations_vec = migrations_slice.to_vec(); - let migrations_item = Migrations::from(migrations_vec); - - let mut encoded_call = vec![51u8, 0]; - encoded_call.extend_from_slice(migrations_item.encode().as_slice()); - let xcm: Xcm<()> = Xcm(vec![ - UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: MAX_WEIGHT, - call: encoded_call.into(), - }, - // ReportTransactStatus should be appended here after knowing the query_id - ]); - - output.push((migrations_item, xcm)); - } + let migrations_item = Migrations::from(migrations.into()); - // TODO: we probably want to ensure we dont build too many messages to overflow the queue. Which we know from the parameter `T::RequiredMaxCapacity`. - // the problem is that we don't know the existing messages in the destination queue. So for now we assume all messages will succeed - output - } - - pub fn mark_migrations_as_sent(project_migration_origins: ProjectMigrationOriginsOf, query_id: QueryId) { - let project_id = project_migration_origins.project_id; - let migration_origins = project_migration_origins.migration_origins; - for MigrationOrigin { user, id, participation_type } in migration_origins { - // match participation_type { - // ParticipationType::Evaluation => { - // Evaluations::::mutate( - // (project_id, T::AccountId32Conversion::convert_back(user), id), - // |maybe_evaluation| { - // if let Some(evaluation) = maybe_evaluation { - // evaluation.ct_migration_status = MigrationStatus::Sent(query_id); - // } - // }, - // ); - // }, - // ParticipationType::Bid => { - // Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { - // if let Some(bid) = maybe_bid { - // bid.ct_migration_status = MigrationStatus::Sent(query_id); - // } - // }); - // }, - // ParticipationType::Contribution => { - // Contributions::::mutate( - // (project_id, T::AccountId32Conversion::convert_back(user), id), - // |maybe_contribution| { - // if let Some(contribution) = maybe_contribution { - // contribution.ct_migration_status = MigrationStatus::Sent(query_id); - // } - // }, - // ); - // }, - // } - } - } - - pub fn mark_migrations_as_confirmed(project_migration_origins: ProjectMigrationOriginsOf) { - let project_id = project_migration_origins.project_id; - let migration_origins = project_migration_origins.migration_origins; - for MigrationOrigin { user, id, participation_type } in migration_origins { - // match participation_type { - // ParticipationType::Evaluation => { - // Evaluations::::mutate( - // (project_id, T::AccountId32Conversion::convert_back(user), id), - // |maybe_evaluation| { - // if let Some(evaluation) = maybe_evaluation { - // evaluation.ct_migration_status = MigrationStatus::Confirmed; - // } - // }, - // ); - // }, - // ParticipationType::Bid => { - // Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { - // if let Some(bid) = maybe_bid { - // bid.ct_migration_status = MigrationStatus::Confirmed; - // } - // }); - // }, - // ParticipationType::Contribution => { - // Contributions::::mutate( - // (project_id, T::AccountId32Conversion::convert_back(user), id), - // |maybe_contribution| { - // if let Some(contribution) = maybe_contribution { - // contribution.ct_migration_status = MigrationStatus::Confirmed; - // } - // }, - // ); - // }, - // } - } + let mut encoded_call = vec![51u8, 0]; + encoded_call.extend_from_slice(migrations_item.encode().as_slice()); + Xcm(vec![ + UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: MAX_WEIGHT, + call: encoded_call.into(), + }, + ReportTransactStatus(QueryResponseInfo { + destination: ParentThen(X1(Parachain(POLIMEC_PARA_ID))).into(), + query_id, + max_weight: MAX_RESPONSE_WEIGHT, + }) + ]) } - pub fn mark_migrations_as_failed( - project_migration_origins: ProjectMigrationOriginsOf, - error: BoundedVec, - ) { - let project_id = project_migration_origins.project_id; - let migration_origins = project_migration_origins.migration_origins; - for MigrationOrigin { user, id, participation_type } in migration_origins { - // match participation_type { - // ParticipationType::Evaluation => { - // Evaluations::::mutate( - // (project_id, T::AccountId32Conversion::convert_back(user), id), - // |maybe_evaluation| { - // if let Some(evaluation) = maybe_evaluation { - // evaluation.ct_migration_status = MigrationStatus::Failed(error.clone()); - // } - // }, - // ); - // }, - // ParticipationType::Bid => { - // Bids::::mutate((project_id, T::AccountId32Conversion::convert_back(user), id), |maybe_bid| { - // if let Some(bid) = maybe_bid { - // bid.ct_migration_status = MigrationStatus::Failed(error.clone()); - // } - // }); - // }, - // ParticipationType::Contribution => { - // Contributions::::mutate( - // (project_id, T::AccountId32Conversion::convert_back(user), id), - // |maybe_contribution| { - // if let Some(contribution) = maybe_contribution { - // contribution.ct_migration_status = MigrationStatus::Failed(error.clone()); - // } - // }, - // ); - // }, - // } - } + fn change_migration_status(project_id: ProjectId, user: T::AccountId, status: MigrationStatus) -> DispatchResult { + let (current_status, migrations) = UserMigrations::::get(project_id, user.clone()).ok_or(Error::::NoMigrationsFound)?; + let status = match status { + MigrationStatus::Sent(_) if matches!(current_status, MigrationStatus::NotStarted | MigrationStatus::Failed) => status, + MigrationStatus::Confirmed if matches!(current_status, MigrationStatus::Sent(_)) => status, + MigrationStatus::Failed if matches!(current_status, MigrationStatus::Sent(_)) => status, + _ => return Err(Error::::NotAllowed.into()), + }; + UserMigrations::::insert(project_id, user, (status, migrations)); + Ok(()) } + } diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index b48dc039a..90d923cd2 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -199,7 +199,6 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use local_macros::*; - use pallet_xcm::Origin; use sp_arithmetic::Percent; use sp_runtime::{ traits::{Convert, ConvertBack, Get}, @@ -548,16 +547,14 @@ use sp_arithmetic::Percent; >; #[pallet::storage] - pub type MigrationQueue = StorageDoubleMap< + pub type UserMigrations = StorageDoubleMap< _, Blake2_128Concat, ProjectId, Blake2_128Concat, T::AccountId, - BoundedVec>, - ValueQuery, + (MigrationStatus, BoundedVec>), >; - pub struct MaxParticipationsPerUser(PhantomData); impl Get for MaxParticipationsPerUser { @@ -566,6 +563,9 @@ use sp_arithmetic::Percent; } } + #[pallet::storage] + pub type ActiveMigrationQueue = StorageMap<_, Blake2_128Concat, QueryId, (ProjectId, T::AccountId), ResultQuery::NoActiveMigrationsFound>>; + #[pallet::storage] /// Migrations sent and awaiting for confirmation pub type UnconfirmedMigrations = StorageMap<_, Blake2_128Concat, QueryId, ProjectMigrationOriginsOf>; @@ -701,21 +701,10 @@ use sp_arithmetic::Percent; query_id: QueryId, response: Response, }, - MigrationStarted { - project_id: ProjectId, - }, - UserMigrationSent { - project_id: ProjectId, - caller: AccountIdOf, - participant: AccountIdOf, - }, - MigrationsConfirmed { - project_id: ProjectId, - migration_origins: BoundedVec>, - }, - MigrationsFailed { + MigrationStatusUpdated { project_id: ProjectId, - migration_origins: BoundedVec>, + account: AccountIdOf, + status: MigrationStatus, }, } @@ -805,12 +794,8 @@ use sp_arithmetic::Percent; PriceNotFound, /// Bond is either lower than the minimum set by the issuer, or the vec is full and can't replace an old one with a lower value EvaluationBondTooLow, - /// Tried to do an operation on an evaluation that does not exist - EvaluationNotFound, /// Tried to do an operation on a finalizer that already finished FinalizerFinished, - /// - ContributionNotFound, /// Tried to start a migration check but the bidirectional channel is not yet open CommsNotEstablished, XcmFailed, @@ -818,10 +803,6 @@ use sp_arithmetic::Percent; BadConversion, /// The issuer doesn't have enough funds (ExistentialDeposit), to create the escrow account NotEnoughFundsForEscrowCreation, - /// The issuer doesn't have enough funds to pay for the metadata of their contribution token - NotEnoughFundsForCTMetadata, - /// The issuer doesn't have enough funds to pay for the deposit of their contribution token - NotEnoughFundsForCTDeposit, /// Too many attempts to insert project in to ProjectsToUpdate storage TooManyInsertionAttempts, /// Reached bid limit for this user on this project @@ -836,6 +817,8 @@ use sp_arithmetic::Percent; TooManyMigrations, /// User has no migrations to execute. NoMigrationsFound, + /// User has no active migrations in the queue. + NoActiveMigrationsFound, // Participant tried to do a community contribution but it already had a winning bid on the auction round. UserHasWinningBids, // Round transition already happened. @@ -1121,15 +1104,6 @@ use sp_arithmetic::Percent; Self::do_migration_check_response(location, query_id, response) } - #[pallet::call_index(25)] - #[pallet::weight(Weight::from_parts(1000, 0))] - pub fn start_migration(origin: OriginFor, jwt: UntrustedToken, project_id: ProjectId) -> DispatchResult { - let (account, _did, investor_type) = - T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); - Self::do_start_migration(&account, project_id) - } - #[pallet::call_index(26)] #[pallet::weight(Weight::from_parts(1000, 0))] pub fn migrate_one_participant( @@ -1137,8 +1111,8 @@ use sp_arithmetic::Percent; project_id: ProjectId, participant: AccountIdOf, ) -> DispatchResult { - let caller = ensure_signed(origin)?; - Self::do_migrate_one_participant(caller, project_id, participant) + let _caller = ensure_signed(origin)?; + Self::do_migrate_one_participant(project_id, participant) } #[pallet::call_index(27)] diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index 5f5fc5226..1c22ba6d6 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -336,7 +336,7 @@ impl Pallet { ct_amount: BalanceOf, vesting_time: BlockNumberFor, ) -> DispatchResult { - MigrationQueue::::try_mutate(project_id, origin, |migrations| -> DispatchResult { + UserMigrations::::try_mutate(project_id, origin, |maybe_migrations| -> DispatchResult { let migration_origin = MigrationOrigin { user: T::AccountId32Conversion::convert(origin.clone()), id: id, @@ -345,7 +345,14 @@ impl Pallet { let vesting_time: u64 = vesting_time.try_into().map_err(|_| Error::::BadMath)?; let migration_info: MigrationInfo = (ct_amount.into(), vesting_time.into()).into(); let migration = Migration::new(migration_origin, migration_info); - migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; + if let Some((_, migrations)) = maybe_migrations { + migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; + } else { + let mut migrations = BoundedVec::<_, MaxParticipationsPerUser>::new(); + migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; + *maybe_migrations = Some((MigrationStatus::NotStarted, migrations)) + } + Ok(()) })?; diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index cb0c37f0d..df72138f1 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -5660,8 +5660,6 @@ mod ct_migration { ); inst.advance_time(::SuccessToSettlementTime::get() + 20u64).unwrap(); - let project_details = inst.get_project_details(project_id); - inst.execute(|| { assert_ok!(crate::Pallet::::do_set_para_id_for_project( &ISSUER, diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index ef411a2f2..76a7608b3 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -19,16 +19,15 @@ //! Types for Funding pallet. use crate::{ - traits::{BondingRequirementCalculation, ProvideAssetPrice, VestingDurationCalculation}, - BalanceOf, BidInfoOf, Config, ContributionInfoOf, EvaluationInfoOf, MultiplierOf, + traits::{BondingRequirementCalculation, ProvideAssetPrice}, + BalanceOf, }; use frame_support::{pallet_prelude::*, traits::tokens::Balance as BalanceT}; use frame_system::pallet_prelude::BlockNumberFor; -use polimec_common::migration_types::{Migration, MigrationInfo, MigrationOrigin, ParticipationType}; use polkadot_parachain::primitives::Id as ParaId; use serde::{Deserialize, Serialize}; use sp_arithmetic::{FixedPointNumber, FixedPointOperand}; -use sp_runtime::traits::{CheckedDiv, Convert, Get, Zero}; +use sp_runtime::traits::{CheckedDiv, Get}; use sp_std::{cmp::Eq, collections::btree_map::*, prelude::*}; pub use config_types::*; @@ -417,8 +416,6 @@ pub mod storage_types { pub mod inner_types { use super::*; use variant_count::VariantCount; - use xcm::v3::MaxDispatchErrorLen; - #[derive(Default, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct CurrencyMetadata { diff --git a/polimec-common/common/src/lib.rs b/polimec-common/common/src/lib.rs index 997b1a28f..87b936b49 100644 --- a/polimec-common/common/src/lib.rs +++ b/polimec-common/common/src/lib.rs @@ -17,7 +17,6 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{pallet_prelude::*, traits::tokens::fungible, RuntimeDebug}; -use itertools::Itertools; use sp_std::prelude::*; pub mod credentials; @@ -146,7 +145,7 @@ pub mod migration_types { NotStarted, Sent(xcm::v3::QueryId), Confirmed, - Failed(BoundedVec), + Failed, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] From 7598d4645c89faef8d60b07ec862d1007573283e Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Fri, 22 Mar 2024 17:36:08 +0100 Subject: [PATCH 13/21] fix some funding end test and remove some others --- integration-tests/src/tests/ct_migration.rs | 488 +--- pallets/funding/src/lib.rs | 7 - pallets/funding/src/tests.rs | 2329 ++----------------- pallets/funding/src/types.rs | 9 - polimec-common/common/src/lib.rs | 8 + runtimes/testnet/src/lib.rs | 6 +- 6 files changed, 321 insertions(+), 2526 deletions(-) diff --git a/integration-tests/src/tests/ct_migration.rs b/integration-tests/src/tests/ct_migration.rs index 34f9d5973..5b95d2899 100644 --- a/integration-tests/src/tests/ct_migration.rs +++ b/integration-tests/src/tests/ct_migration.rs @@ -16,22 +16,16 @@ use crate::*; use pallet_funding::{ - assert_close_enough, traits::VestingDurationCalculation, AcceptedFundingAsset, BidStatus, EvaluatorsOutcome, - Multiplier, MultiplierOf, ProjectId, RewardOrSlash, + assert_close_enough, ProjectId, }; use polimec_common::{ - credentials::InvestorType, - migration_types::{Migration, MigrationInfo, MigrationOrigin, Migrations, MigrationStatus, ParticipationType}, + migration_types::{Migrations, MigrationStatus}, }; use polimec_parachain_runtime::PolimecFunding; -use sp_runtime::{traits::Convert, FixedPointNumber, Perquintill}; +use sp_runtime::Perquintill; use std::collections::HashMap; use tests::defaults::*; -fn execute_cleaner(inst: &mut IntegrationInstantiator) { - Polimec::execute_with(|| { - inst.advance_time(::SuccessToSettlementTime::get() + 1u32).unwrap(); - }); -} + fn mock_hrmp_establishment(project_id: u32) { Polimec::execute_with(|| { assert_ok!(PolimecFunding::do_set_para_id_for_project(&ISSUER.into(), project_id, ParaId::from(6969u32),)); @@ -46,11 +40,6 @@ fn mock_hrmp_establishment(project_id: u32) { let channel_accepted_message = xcm::v3::opaque::Instruction::HrmpChannelAccepted { recipient: 6969u32 }; assert_ok!(PolimecFunding::do_handle_channel_accepted(channel_accepted_message)); }); - - Penpal::execute_with(|| { - println!("penpal events:"); - dbg!(Penpal::events()); - }); } fn assert_migration_is_ready(project_id: u32) { @@ -60,428 +49,153 @@ fn assert_migration_is_ready(project_id: u32) { }); } -fn send_migrations(project_id: ProjectId, accounts: Vec) -> HashMap { - let mut output = HashMap::new(); - for account in accounts { - let migrations = Polimec::execute_with(|| { +fn get_migrations_for_participants(project_id: ProjectId, participants: Vec) -> HashMap { + let mut user_migrations = HashMap::new(); + Polimec::execute_with(|| { + for participant in participants { + let (status, migrations) = pallet_funding::UserMigrations::::get(project_id, participant.clone()).unwrap(); + user_migrations.insert(participant, (status, Migrations::from(migrations.into()))); + } + }); + user_migrations +} + +fn send_migrations(project_id: ProjectId, accounts: Vec) { + for user in accounts.into_iter() { + Polimec::execute_with(|| { assert_ok!(PolimecFunding::migrate_one_participant( - PolimecOrigin::signed(account.clone()), + PolimecOrigin::signed(user.clone()), project_id, - account.clone() + user.clone() )); - - let user_evaluations = - pallet_funding::Evaluations::::iter_prefix_values((project_id, account.clone())); - let user_bids = pallet_funding::Bids::::iter_prefix_values((project_id, account.clone())) - .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))); - let user_contributions = - pallet_funding::Contributions::::iter_prefix_values((project_id, account.clone())); - - let evaluation_migrations = user_evaluations.map(|evaluation| { - let evaluator_bytes = ::AccountId32Conversion::convert( - evaluation.evaluator.clone(), - ); - assert!( - matches!(evaluation.ct_migration_status, MigrationStatus::Sent(_)), - "{:?}'s evaluation was not sent {:?}", - names()[&evaluator_bytes], - evaluation - ); - if let Some(RewardOrSlash::Reward(amount)) = evaluation.rewarded_or_slashed { - Migration { - info: MigrationInfo { - contribution_token_amount: amount, - vesting_time: Multiplier::new(1u8) - .unwrap() - .calculate_vesting_duration::() - .into(), - }, - origin: MigrationOrigin { - user: account.clone().into(), - id: evaluation.id, - participation_type: ParticipationType::Evaluation, - }, - } - } else { - panic!("should be rewarded") - } - }); - let bid_migrations = user_bids.map(|bid| { - assert!(matches!(bid.ct_migration_status, MigrationStatus::Sent(_))); - Migration { - info: MigrationInfo { - contribution_token_amount: bid.final_ct_amount, - vesting_time: bid.multiplier.calculate_vesting_duration::().into(), - }, - origin: MigrationOrigin { - user: account.clone().into(), - id: bid.id, - participation_type: ParticipationType::Bid, - }, - } - }); - let contribution_migrations = user_contributions.map(|contribution| { - assert!(matches!(contribution.ct_migration_status, MigrationStatus::Sent(_))); - Migration { - info: MigrationInfo { - contribution_token_amount: contribution.ct_amount, - vesting_time: contribution.multiplier.calculate_vesting_duration::().into(), - }, - origin: MigrationOrigin { - user: account.clone().into(), - id: contribution.id, - participation_type: ParticipationType::Contribution, - }, - } - }); - - evaluation_migrations.chain(bid_migrations).chain(contribution_migrations).collect::() }); - if migrations.clone().inner().is_empty() { - panic!("no migrations for account: {:?}", account) - } - output.insert(account.clone(), migrations); } - output } -fn migrations_are_executed(grouped_migrations: Vec) { - let all_migrations = - grouped_migrations.iter().flat_map(|migrations| migrations.clone().inner()).collect::>(); - Penpal::execute_with(|| { - assert_expected_events!( - Penpal, - vec![ - PenpalEvent::PolimecReceiver(polimec_receiver::Event::MigrationExecuted{migration}) => { - migration: all_migrations.contains(&migration), - }, - ] - ); - }); - - // since current way to migrate is by bundling each user's migrations into one, we can assume that all migrations in a group are from the same user - for migration_group in grouped_migrations { - let user = migration_group.clone().inner()[0].origin.user; - assert!(migration_group.origins().iter().all(|origin| origin.user == user)); - - let user_info = Penpal::account_data_of(user.into()); - assert_close_enough!(user_info.free, migration_group.total_ct_amount(), Perquintill::from_float(0.99)); - - let vest_scheduled_cts = migration_group - .inner() - .iter() - .filter_map(|migration| { - if migration.info.vesting_time > 1 { - Some(migration.info.contribution_token_amount) - } else { - None +fn migrations_are_executed(project_id: ProjectId, accounts: Vec) { + let user_migrations = get_migrations_for_participants(project_id, accounts.clone()); + for account in accounts.into_iter(){ + let user_info = Penpal::account_data_of(account.clone()); + Penpal::execute_with(|| { + + let (_, migrations) = user_migrations.get(&account).unwrap(); + let matched_events = Penpal::events().iter().filter(|event| { + match event { + PenpalEvent::PolimecReceiver(polimec_receiver::Event::MigrationExecuted{migration}) => { + migrations.contains(&migration) + }, + _ => false, } - }) - .sum::(); - assert_close_enough!(user_info.frozen, vest_scheduled_cts, Perquintill::from_float(0.99)); + }).count(); + assert_eq!(matched_events, migrations.len()); + + assert_close_enough!(user_info.free, migrations.total_ct_amount(), Perquintill::from_float(0.99)); + + let vest_scheduled_cts = migrations.clone() + .inner() + .iter() + .filter_map(|migration| { + if migration.info.vesting_time > 1 { + Some(migration.info.contribution_token_amount) + } else { + None + } + }) + .sum::(); + assert_close_enough!(user_info.frozen, vest_scheduled_cts, Perquintill::from_float(0.99)); + }); } } -fn migrations_are_confirmed(project_id: u32, grouped_migrations: Vec) { - let ordered_grouped_origins = grouped_migrations - .clone() - .into_iter() - .map(|group| { - let mut origins = group.origins(); - origins.sort(); - origins - }) - .collect::>(); +fn migrations_are_confirmed(project_id: u32, accounts: Vec) { + let user_migrations = get_migrations_for_participants(project_id, accounts.clone()); Polimec::execute_with(|| { - assert_expected_events!( - Polimec, - vec![ - PolimecEvent::PolimecFunding(pallet_funding::Event::MigrationsConfirmed{project_id, migration_origins}) => { - project_id: project_id == project_id, - migration_origins: { - let mut migration_origins = migration_origins.to_vec(); - migration_origins.sort(); - ordered_grouped_origins.contains(&migration_origins) + for user in accounts.iter() { + let (current_status, _) = user_migrations.get(user).unwrap(); + assert_eq!(current_status, &MigrationStatus::Confirmed); + + let matched_events: usize = Polimec::events().iter().filter(|event| { + match event { + PolimecEvent::PolimecFunding(pallet_funding::Event::MigrationStatusUpdated{project_id, account, status}) => { + project_id == project_id && account == user && + matches!(status, &MigrationStatus::Confirmed) }, - }, - ] - ); - let all_migration_origins = - grouped_migrations.iter().flat_map(|migrations| migrations.clone().origins()).collect::>(); - for migration_origin in all_migration_origins { - match migration_origin.participation_type { - ParticipationType::Evaluation => { - let evaluation = pallet_funding::Evaluations::::get(( - project_id, - AccountId::from(migration_origin.user), - migration_origin.id, - )) - .unwrap(); - assert_eq!(evaluation.ct_migration_status, MigrationStatus::Confirmed); - }, - ParticipationType::Bid => { - let bid = pallet_funding::Bids::::get(( - project_id, - AccountId::from(migration_origin.user), - migration_origin.id, - )) - .unwrap(); - assert_eq!(bid.ct_migration_status, MigrationStatus::Confirmed); - }, - ParticipationType::Contribution => { - let contribution = pallet_funding::Contributions::::get(( - project_id, - AccountId::from(migration_origin.user), - migration_origin.id, - )) - .unwrap(); - assert_eq!(contribution.ct_migration_status, MigrationStatus::Confirmed); - }, - } + _ => false, + } + }).count(); + assert_eq!(matched_events, 1); + + } }); } -fn vest_migrations(grouped_migrations: Vec) { - let biggest_time = grouped_migrations.iter().map(|migrations| migrations.biggest_vesting_time()).max().unwrap(); +fn vest_migrations(project_id: u32, accounts: Vec) { + let user_migrations = get_migrations_for_participants(project_id, accounts.clone()); + let biggest_time = user_migrations.iter().map(|(_, (_, migrations))| { + migrations.biggest_vesting_time() + }).max().unwrap(); + Penpal::execute_with(|| { PenpalSystem::set_block_number(biggest_time as u32 + 1u32); }); - for migration_group in grouped_migrations { - let user = migration_group.clone().inner()[0].origin.user; - assert!(migration_group.origins().iter().all(|origin| origin.user == user)); - // check if any vesting_time is bigger than 1, which means the balance was actually frozen - let has_frozen_balance = migration_group.inner().iter().any(|migration| migration.info.vesting_time > 1); - if has_frozen_balance { - Penpal::execute_with(|| { - assert_ok!(pallet_vesting::Pallet::::vest(PenpalOrigin::signed(user.into()))); - }); - } + for account in accounts { + let user_info = Penpal::account_data_of(account.clone()); + Penpal::execute_with(|| { + if user_info.frozen > 0 { + assert_ok!(pallet_vesting::Pallet::::vest(PenpalOrigin::signed(account))); + } + }); } } -fn migrations_are_vested(grouped_migrations: Vec) { - for migration_group in grouped_migrations { - let user = migration_group.clone().inner()[0].origin.user; - assert!(migration_group.origins().iter().all(|origin| origin.user == user)); - let user_info = Penpal::account_data_of(user.into()); +fn migrations_are_vested(project_id: u32, accounts: Vec) { + let user_migrations = get_migrations_for_participants(project_id, accounts.clone()); + user_migrations.iter().for_each(|(user, (_, migrations))| { + let user_info = Penpal::account_data_of(user.clone()); assert_eq!(user_info.frozen, 0); - assert_eq!(user_info.free, migration_group.total_ct_amount()); - } + assert_eq!(user_info.free, migrations.clone().total_ct_amount()); + }); } -#[test] -fn migration_check() { +fn create_settled_project() -> ProjectId { let mut inst = IntegrationInstantiator::new(None); - let project_id = Polimec::execute_with(|| { + Polimec::execute_with(|| { let project_id = inst.create_finished_project( default_project_metadata(0, ISSUER.into()), ISSUER.into(), default_evaluations(), default_bids(), default_community_contributions(), - vec![], + default_remainder_contributions(), ); - - inst.advance_time(::SuccessToSettlementTime::get() + 1u32).unwrap(); + inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + inst.settle_project(project_id).unwrap(); project_id - }); - - mock_hrmp_establishment(project_id); - - assert_migration_is_ready(project_id); -} - -#[test] -fn migration_is_sent() { - let mut inst = IntegrationInstantiator::new(None); - let participants = - vec![EVAL_1, EVAL_2, EVAL_3, BIDDER_1, BIDDER_2, BIDDER_3, BIDDER_4, BUYER_1, BUYER_2, BUYER_3, BUYER_4] - .into_iter() - .map(|x| AccountId::from(x)) - .collect::>(); - let project_id = Polimec::execute_with(|| { - inst.create_finished_project( - default_project_metadata(0, ISSUER.into()), - ISSUER.into(), - default_evaluations(), - default_bids(), - default_community_contributions(), - default_remainder_contributions(), - ) - }); - execute_cleaner(&mut inst); - - mock_hrmp_establishment(project_id); - - assert_migration_is_ready(project_id); - - send_migrations(project_id, participants); + }) } #[test] -fn migration_is_executed_on_project_and_confirmed_on_polimec() { - let mut inst = IntegrationInstantiator::new(None); - let participants = - vec![EVAL_1, EVAL_2, EVAL_3, BIDDER_2, BIDDER_3, BIDDER_4, BIDDER_5, BUYER_2, BUYER_3, BUYER_4, BUYER_5] - .into_iter() - .map(|x| AccountId::from(x)) - .collect::>(); - let project_id = Polimec::execute_with(|| { - inst.create_finished_project( - default_project_metadata(0, ISSUER.into()), - ISSUER.into(), - default_evaluations(), - default_bids(), - default_community_contributions(), - default_remainder_contributions(), - ) - }); - execute_cleaner(&mut inst); - - mock_hrmp_establishment(project_id); - - assert_migration_is_ready(project_id); - - let migrations_map = send_migrations(project_id, participants); - let grouped_migrations = migrations_map.values().cloned().collect::>(); +fn full_migration_test() { + let project_id = create_settled_project(); - migrations_are_executed(grouped_migrations.clone()); - - migrations_are_confirmed(project_id, grouped_migrations.clone()); -} - -#[test] -fn vesting_over_several_blocks_on_project() { - let mut inst = IntegrationInstantiator::new(None); let participants = vec![EVAL_1, EVAL_2, EVAL_3, BIDDER_1, BIDDER_2, BUYER_1, BUYER_2] .into_iter() .map(|x| AccountId::from(x)) .collect::>(); - let bids = default_bids(); - let community_contributions = default_community_contributions(); - let remainder_contributions = default_remainder_contributions(); - - let project_id = Polimec::execute_with(|| { - inst.create_finished_project( - default_project_metadata(0, ISSUER.into()), - ISSUER.into(), - default_evaluations(), - bids, - community_contributions, - remainder_contributions, - ) - }); - execute_cleaner(&mut inst); - - mock_hrmp_establishment(project_id); - - assert_migration_is_ready(project_id); - - // Migrate is sent - let user_migrations = send_migrations(project_id, participants); - let grouped_migrations = user_migrations.values().cloned().collect::>(); - - migrations_are_executed(grouped_migrations.clone()); - - migrations_are_confirmed(project_id, grouped_migrations.clone()); - - vest_migrations(grouped_migrations.clone()); - - migrations_are_vested(grouped_migrations.clone()); -} - -#[test] -fn disallow_duplicated_migrations_on_receiver_pallet() { - let mut inst = IntegrationInstantiator::new(None); - - let project_id = Polimec::execute_with(|| { - inst.create_finished_project( - default_project_metadata(0, ISSUER.into()), - ISSUER.into(), - default_evaluations(), - default_bids(), - default_community_contributions(), - default_remainder_contributions(), - ) - }); - - let project_details = Polimec::execute_with(|| inst.get_project_details(project_id)); - if let EvaluatorsOutcome::Rewarded(info) = project_details.evaluation_round_info.evaluators_outcome { - println!("rewarded: {:?}", info); - } else { - panic!("should be rewarded") - } - - let participants = vec![ - EVAL_1, EVAL_2, EVAL_3, EVAL_4, BIDDER_1, BIDDER_2, BIDDER_3, BIDDER_4, BIDDER_5, BIDDER_6, BUYER_1, BUYER_2, - BUYER_3, BUYER_4, BUYER_5, BUYER_6, - ] - .into_iter() - .map(|x| AccountId::from(x)) - .collect::>(); - - execute_cleaner(&mut inst); - mock_hrmp_establishment(project_id); assert_migration_is_ready(project_id); // Migrate is sent - let user_migrations = send_migrations(project_id, participants); - let grouped_migrations = user_migrations.values().cloned().collect::>(); - - migrations_are_executed(grouped_migrations.clone()); - - migrations_are_confirmed(project_id, grouped_migrations.clone()); + send_migrations(project_id, participants.clone()); - vest_migrations(grouped_migrations.clone()); + migrations_are_executed(project_id, participants.clone()); - migrations_are_vested(grouped_migrations.clone()); + migrations_are_confirmed(project_id, participants.clone()); - // just any number that lets us execute our xcm's - for migrations in grouped_migrations.clone() { - for (_, xcm) in PolimecFundingPallet::construct_migration_xcm_messages(migrations) { - let _call: ::RuntimeCall = - pallet_funding::Call::confirm_migrations { query_id: Default::default(), response: Default::default() } - .into(); - - let max_weight = Weight::from_parts(700_000_000, 10_000); - let mut instructions = xcm.into_inner(); - instructions.push(ReportTransactStatus(QueryResponseInfo { - destination: ParentThen(X1(Parachain(Polimec::para_id().into()))).into(), - query_id: 69, - max_weight, - })); - let xcm = Xcm(instructions); - let project_multilocation = MultiLocation { parents: 1, interior: X1(Parachain(Penpal::para_id().into())) }; - - Polimec::execute_with(|| { - PolimecXcmPallet::send_xcm(Here, project_multilocation, xcm).unwrap(); - }); - } - } + vest_migrations(project_id, participants.clone()); - // each duplicated migration was skipped (in this case we duplicated all of them) - let all_migrations = - grouped_migrations.iter().flat_map(|migrations| migrations.clone().inner()).collect::>(); - Penpal::execute_with(|| { - assert_expected_events!( - Penpal, - vec![ - PenpalEvent::PolimecReceiver(polimec_receiver::Event::DuplicatedMigrationSkipped{migration}) => { - migration: all_migrations.contains(&migration), - }, - ] - ); - }); - - migrations_are_vested(grouped_migrations.clone()); -} - -#[ignore] -#[test] -fn failing_bid_doesnt_get_migrated() { - todo!(); -} + migrations_are_vested(project_id, participants.clone()); +} \ No newline at end of file diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 90d923cd2..c780fe033 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -179,9 +179,6 @@ pub type BidInfoOf = pub type ContributionInfoOf = ContributionInfo, BalanceOf, MultiplierOf>; -pub type ProjectMigrationOriginsOf = - ProjectMigrationOrigins>>; - pub type BucketOf = Bucket, PriceOf>; pub type WeightInfoOf = ::WeightInfo; @@ -566,10 +563,6 @@ use sp_arithmetic::Percent; #[pallet::storage] pub type ActiveMigrationQueue = StorageMap<_, Blake2_128Concat, QueryId, (ProjectId, T::AccountId), ResultQuery::NoActiveMigrationsFound>>; - #[pallet::storage] - /// Migrations sent and awaiting for confirmation - pub type UnconfirmedMigrations = StorageMap<_, Blake2_128Concat, QueryId, ProjectMigrationOriginsOf>; - #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] pub enum Event { diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index df72138f1..1c2c19c45 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -3527,2126 +3527,221 @@ mod remainder_contribution { } // only functionalities that happen after the REMAINDER FUNDING period of a project, and before the CT Migration -// mod funding_end { -// use super::*; - -// #[test] -// fn automatic_fail_less_eq_33_percent() { -// for funding_percent in (1..=33).step_by(5) { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); -// let min_price = project_metadata.minimum_price; -// let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * -// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// vec![100u8], -// vec![BIDDER_1], -// vec![10u8], -// ); -// let contributions = MockInstantiator::generate_contributions_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_community_contributors(), -// default_multipliers(), -// ); -// let project_id = -// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); -// } -// } - -// #[test] -// fn automatic_success_bigger_eq_90_percent() { -// for funding_percent in (90..=100).step_by(2) { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); -// let min_price = project_metadata.minimum_price; -// let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * -// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_bidders(), -// default_multipliers(), -// ); -// let contributions = MockInstantiator::generate_contributions_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_community_contributors(), -// default_multipliers(), -// ); -// let project_id = -// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); -// } -// } - -// #[test] -// fn manual_outcome_above33_to_below90() { -// for funding_percent in (34..90).step_by(5) { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); -// let min_price = project_metadata.minimum_price; -// let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * -// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_bidders(), -// default_multipliers(), -// ); -// let contributions = MockInstantiator::generate_contributions_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_community_contributors(), -// default_multipliers(), -// ); -// let project_id = -// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); -// } -// } - -// #[test] -// fn manual_acceptance() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); -// let min_price = project_metadata.minimum_price; -// let twenty_percent_funding_usd = Perquintill::from_percent(55) * -// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_bidders(), -// default_multipliers(), -// ); -// let contributions = MockInstantiator::generate_contributions_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_community_contributors(), -// default_multipliers(), -// ); -// let project_id = -// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - -// let project_id = project_id; -// inst.execute(|| { -// PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::AcceptFunding) -// }) -// .unwrap(); - -// inst.advance_time(1u64).unwrap(); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - -// assert_matches!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Initialized(_))); -// inst.test_ct_created_for(project_id); - -// inst.advance_time(10u64).unwrap(); -// assert_matches!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Success(CleanerState::Finished(PhantomData)) -// ); -// } - -// #[test] -// fn manual_rejection() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); -// let min_price = project_metadata.minimum_price; -// let twenty_percent_funding_usd = Perquintill::from_percent(55) * -// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_bidders(), -// default_multipliers(), -// ); -// let contributions = MockInstantiator::generate_contributions_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_community_contributors(), -// default_multipliers(), -// ); -// let project_id = -// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - -// let project_id = project_id; -// inst.execute(|| { -// PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::RejectFunding) -// }) -// .unwrap(); - -// inst.advance_time(1u64).unwrap(); - -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// assert_matches!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Failure(CleanerState::Initialized(PhantomData)) -// ); - -// inst.test_ct_not_created_for(project_id); - -// inst.advance_time(10u64).unwrap(); -// assert_matches!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Failure(CleanerState::Finished(PhantomData)) -// ); -// } - -// #[test] -// fn automatic_acceptance_on_manual_decision_after_time_delta() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); -// let min_price = project_metadata.minimum_price; -// let twenty_percent_funding_usd = Perquintill::from_percent(55) * -// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_bidders(), -// default_multipliers(), -// ); -// let contributions = MockInstantiator::generate_contributions_from_total_usd( -// Percent::from_percent(50u8) * twenty_percent_funding_usd, -// min_price, -// default_weights(), -// default_community_contributors(), -// default_multipliers(), -// ); -// let project_id = -// inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - -// let project_id = project_id; -// inst.advance_time(1u64 + ::ManualAcceptanceDuration::get()).unwrap(); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - -// inst.test_ct_created_for(project_id); - -// inst.settle_project(project_id).unwrap(); -// } - -// #[test] -// fn evaluators_get_slashed_funding_accepted() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_id = project_from_funding_reached(&mut inst, 43u64); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - -// let old_evaluation_locked_plmc = inst -// .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) -// .into_iter() -// .filter(|item| item.plmc_amount > Zero::zero()) -// .collect::>>(); - -// let evaluators = old_evaluation_locked_plmc.accounts(); - -// let old_participation_locked_plmc = -// inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); -// let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); - -// call_and_is_ok!( -// inst, -// PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::AcceptFunding) -// ); -// inst.advance_time(1u64).unwrap(); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); -// inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); -// assert_matches!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Success(CleanerState::Finished(PhantomData)) -// ); - -// let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); -// let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( -// vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], -// MergeOperation::Add, -// ); - -// let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); - -// assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); -// } - -// #[test] -// fn evaluators_get_slashed_funding_funding_rejected() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_id = project_from_funding_reached(&mut inst, 56u64); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); - -// let old_evaluation_locked_plmc = inst -// .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) -// .into_iter() -// .filter(|item| item.plmc_amount > Zero::zero()) -// .collect::>>(); - -// let evaluators = old_evaluation_locked_plmc.accounts(); - -// let old_participation_locked_plmc = -// inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); -// let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); - -// call_and_is_ok!( -// inst, -// PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::RejectFunding) -// ); -// inst.advance_time(1u64).unwrap(); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); -// inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); -// assert_matches!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Failure(CleanerState::Finished(PhantomData)) -// ); - -// let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); -// let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( -// vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], -// MergeOperation::Add, -// ); -// let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); - -// assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); -// } - -// #[test] -// fn evaluators_get_slashed_funding_failed() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let project_id = project_from_funding_reached(&mut inst, 24u64); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - -// let old_evaluation_locked_plmc = inst -// .get_all_reserved_plmc_balances(HoldReason::Evaluation(project_id).into()) -// .into_iter() -// .filter(|item| item.plmc_amount > Zero::zero()) -// .collect::>(); - -// let evaluators = old_evaluation_locked_plmc.accounts(); - -// let old_participation_locked_plmc = -// inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Participation(project_id).into()); -// let old_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); - -// inst.advance_time(::SuccessToSettlementTime::get() + 10u64).unwrap(); -// assert_matches!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Failure(CleanerState::Finished(PhantomData)) -// ); - -// let slashed_evaluation_locked_plmc = MockInstantiator::slash_evaluator_balances(old_evaluation_locked_plmc); -// let expected_evaluator_free_balances = MockInstantiator::generic_map_operation( -// vec![slashed_evaluation_locked_plmc, old_participation_locked_plmc, old_free_plmc], -// MergeOperation::Add, -// ); - -// let actual_evaluator_free_balances = inst.get_free_plmc_balances_for(evaluators.clone()); - -// assert_eq!(actual_evaluator_free_balances, expected_evaluator_free_balances); -// } - -// #[test] -// fn ct_minted_automatically() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations.clone(), -// bids.clone(), -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.status, ProjectStatus::FundingSuccessful); - -// let reward_info = match details.evaluation_round_info.evaluators_outcome { -// EvaluatorsOutcome::Rewarded(reward_info) => Some(reward_info), -// _ => None, -// }; - -// let evaluators = evaluations.accounts(); -// let evaluator_ct_amounts = evaluators -// .iter() -// .map(|account| { -// let evaluations = inst.execute(|| { -// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// let total_evaluator_ct_rewarded = match reward_info.clone() { -// Some(info) => { -// evaluations -// .iter() -// .map(|evaluation| { -// Pallet::::calculate_evaluator_reward(evaluation, &info) -// }) -// .sum::() -// }, -// None => Zero::zero() -// }; - -// (account, total_evaluator_ct_rewarded) -// }) -// .collect_vec(); - -// let bidders = bids.accounts(); -// let bidder_ct_amounts = bidders -// .iter() -// .map(|account| { -// let bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); - -// (account, total_bidder_ct_rewarded) -// }) -// .collect_vec(); - -// let community_accounts = community_contributions.accounts(); -// let remainder_accounts = remainder_contributions.accounts(); -// let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); -// let contributor_ct_amounts = all_contributors -// .map(|account| { -// let contributions = inst.execute(|| { -// Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// let total_contributor_ct_rewarded = -// contributions.iter().map(|contribution| contribution.ct_amount).sum::(); - -// (account, total_contributor_ct_rewarded) -// }) -// .collect_vec(); - -// let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( -// vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], -// |item| item.0, -// Zero::zero(), -// |item, accumulator| accumulator + item.1, -// ); - -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - -// inst.settle_project(project_id).unwrap(); - -// for (account, amount) in all_ct_expectations { -// let minted = -// inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); -// assert_eq!(minted, amount); -// } -// } - -// #[test] -// fn ct_minted_manually() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations.clone(), -// bids.clone(), -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.status, ProjectStatus::FundingSuccessful); -// assert_eq!(details.cleanup, Cleaner::NotReady); -// // do_end_funding -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// assert_eq!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Success(CleanerState::Initialized(PhantomData)) -// ); - -// let evaluators = evaluations.accounts(); -// let evaluator_ct_amounts = evaluators -// .iter() -// .map(|account| { -// let evaluations = inst.execute(|| { -// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for evaluation in evaluations.iter() { -// inst.execute(|| { -// assert_ok!(Pallet::::evaluation_reward_payout_for( -// RuntimeOrigin::signed(evaluation.evaluator), -// project_id, -// evaluation.evaluator, -// evaluation.id, -// )); -// }); -// } -// let evaluations = inst.execute(|| { -// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// let total_evaluator_ct_rewarded = evaluations -// .iter() -// .map(|evaluation| evaluation.rewarded_or_slashed) -// .map(|reward_or_slash| { -// if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { -// balance -// } else { -// Zero::zero() -// } -// }) -// .sum::(); - -// (account, total_evaluator_ct_rewarded) -// }) -// .collect_vec(); - -// let bidders = bids.accounts(); -// let bidder_ct_amounts = bidders -// .iter() -// .map(|account| { -// let bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for bid in bids.iter() { -// inst.execute(|| { -// assert_ok!(Pallet::::bid_ct_mint_for( -// RuntimeOrigin::signed(bid.bidder), -// project_id, -// bid.bidder, -// bid.id, -// )); -// }); -// } - -// let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); - -// (account, total_bidder_ct_rewarded) -// }) -// .collect_vec(); - -// let community_accounts = community_contributions.accounts(); -// let remainder_accounts = remainder_contributions.accounts(); -// let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); -// let contributor_ct_amounts = all_contributors -// .map(|account| { -// let contributions = inst.execute(|| { -// Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for contribution in contributions.iter() { -// inst.execute(|| { -// assert_ok!(Pallet::::contribution_ct_mint_for( -// RuntimeOrigin::signed(contribution.contributor), -// project_id, -// contribution.contributor, -// contribution.id, -// )); -// }); -// } - -// let total_contributor_ct_rewarded = -// contributions.iter().map(|contribution| contribution.ct_amount).sum::(); - -// (account, total_contributor_ct_rewarded) -// }) -// .collect_vec(); - -// let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( -// vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], -// |item| item.0, -// Zero::zero(), -// |item, accumulator| accumulator + item.1, -// ); - -// for (account, amount) in all_ct_expectations { -// let minted = -// inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); -// assert_eq!(minted, amount, "Account: {}", account); -// } - -// let details = inst.get_project_details(project_id); -// assert_eq!(details.status, ProjectStatus::FundingSuccessful); -// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); -// } - -// #[test] -// fn cannot_mint_ct_twice_manually() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations.clone(), -// bids.clone(), -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.status, ProjectStatus::FundingSuccessful); -// assert_eq!(details.cleanup, Cleaner::NotReady); -// // do_end_funding -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// assert_eq!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Success(CleanerState::Initialized(PhantomData)) -// ); - -// let evaluators = evaluations.accounts(); -// let evaluator_ct_amounts = evaluators -// .iter() -// .map(|account| { -// let evaluations = inst.execute(|| { -// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for evaluation in evaluations.iter() { -// inst.execute(|| { -// assert_ok!(Pallet::::evaluation_reward_payout_for( -// RuntimeOrigin::signed(evaluation.evaluator), -// project_id, -// evaluation.evaluator, -// evaluation.id, -// )); -// assert_noop!( -// Pallet::::evaluation_reward_payout_for( -// RuntimeOrigin::signed(evaluation.evaluator), -// project_id, -// evaluation.evaluator, -// evaluation.id, -// ), -// Error::::NotAllowed -// ); -// }); -// } -// let evaluations = inst.execute(|| { -// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// let total_evaluator_ct_rewarded = evaluations -// .iter() -// .map(|evaluation| evaluation.rewarded_or_slashed) -// .map(|reward_or_slash| { -// if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { -// balance -// } else { -// Zero::zero() -// } -// }) -// .sum::(); - -// (account, total_evaluator_ct_rewarded) -// }) -// .collect_vec(); - -// let bidders = bids.accounts(); -// let bidder_ct_amounts = bidders -// .iter() -// .map(|account| { -// let bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for bid in bids.iter() { -// inst.execute(|| { -// assert_ok!(Pallet::::bid_ct_mint_for( -// RuntimeOrigin::signed(bid.bidder), -// project_id, -// bid.bidder, -// bid.id, -// )); -// }); -// inst.execute(|| { -// assert_noop!( -// Pallet::::bid_ct_mint_for( -// RuntimeOrigin::signed(bid.bidder), -// project_id, -// bid.bidder, -// bid.id, -// ), -// Error::::NotAllowed -// ); -// }); -// } - -// let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); - -// (account, total_bidder_ct_rewarded) -// }) -// .collect_vec(); - -// let community_accounts = community_contributions.accounts(); -// let remainder_accounts = remainder_contributions.accounts(); -// let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); -// let contributor_ct_amounts = all_contributors -// .map(|account| { -// let contributions = inst.execute(|| { -// Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for contribution in contributions.iter() { -// inst.execute(|| { -// assert_ok!(Pallet::::contribution_ct_mint_for( -// RuntimeOrigin::signed(contribution.contributor), -// project_id, -// contribution.contributor, -// contribution.id, -// )); -// }); -// inst.execute(|| { -// assert_noop!( -// Pallet::::contribution_ct_mint_for( -// RuntimeOrigin::signed(contribution.contributor), -// project_id, -// contribution.contributor, -// contribution.id, -// ), -// Error::::NotAllowed -// ); -// }); -// } - -// let total_contributor_ct_rewarded = -// contributions.iter().map(|contribution| contribution.ct_amount).sum::(); - -// (account, total_contributor_ct_rewarded) -// }) -// .collect_vec(); - -// let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( -// vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], -// |item| item.0, -// Zero::zero(), -// |item, accumulator| accumulator + item.1, -// ); - -// for (account, amount) in all_ct_expectations { -// let minted = -// inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); -// assert_eq!(minted, amount, "Account: {}", account); -// } - -// let details = inst.get_project_details(project_id); -// assert_eq!(details.status, ProjectStatus::FundingSuccessful); -// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); -// } - -// #[test] -// fn cannot_mint_ct_manually_after_automatic_mint() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations.clone(), -// bids.clone(), -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.status, ProjectStatus::FundingSuccessful); -// assert_eq!(details.cleanup, Cleaner::NotReady); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// assert_eq!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Success(CleanerState::Initialized(PhantomData)) -// ); -// inst.advance_time(1).unwrap(); -// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - -// let evaluators = evaluations.accounts(); -// let evaluator_ct_amounts = evaluators -// .iter() -// .map(|account| { -// let evaluations = inst.execute(|| { -// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for evaluation in evaluations.iter() { -// inst.execute(|| { -// assert_noop!( -// Pallet::::evaluation_reward_payout_for( -// RuntimeOrigin::signed(evaluation.evaluator), -// project_id, -// evaluation.evaluator, -// evaluation.id, -// ), -// Error::::NotAllowed -// ); -// }); -// } -// let evaluations = inst.execute(|| { -// Evaluations::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// let total_evaluator_ct_rewarded = evaluations -// .iter() -// .map(|evaluation| evaluation.rewarded_or_slashed) -// .map(|reward_or_slash| { -// if let Some(RewardOrSlash::Reward(balance)) = reward_or_slash { -// balance -// } else { -// Zero::zero() -// } -// }) -// .sum::(); - -// (account, total_evaluator_ct_rewarded) -// }) -// .collect_vec(); - -// let bidders = bids.accounts(); -// let bidder_ct_amounts = bidders -// .iter() -// .map(|account| { -// let bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for bid in bids.iter() { -// inst.execute(|| { -// assert_noop!( -// Pallet::::bid_ct_mint_for( -// RuntimeOrigin::signed(bid.bidder), -// project_id, -// bid.bidder, -// bid.id, -// ), -// Error::::NotAllowed -// ); -// }); -// } - -// let total_bidder_ct_rewarded = bids.iter().map(|bid| bid.final_ct_amount).sum::(); - -// (account, total_bidder_ct_rewarded) -// }) -// .collect_vec(); - -// let community_accounts = community_contributions.accounts(); -// let remainder_accounts = remainder_contributions.accounts(); -// let all_contributors = community_accounts.iter().chain(remainder_accounts.iter()).unique(); -// let contributor_ct_amounts = all_contributors -// .map(|account| { -// let contributions = inst.execute(|| { -// Contributions::::iter_prefix_values((project_id, account.clone())).collect::>() -// }); -// for contribution in contributions.iter() { -// inst.execute(|| { -// assert_noop!( -// Pallet::::contribution_ct_mint_for( -// RuntimeOrigin::signed(contribution.contributor), -// project_id, -// contribution.contributor, -// contribution.id, -// ), -// Error::::NotAllowed -// ); -// }); -// } - -// let total_contributor_ct_rewarded = -// contributions.iter().map(|contribution| contribution.ct_amount).sum::(); - -// (account, total_contributor_ct_rewarded) -// }) -// .collect_vec(); - -// let all_ct_expectations = MockInstantiator::generic_map_merge_reduce( -// vec![evaluator_ct_amounts, bidder_ct_amounts, contributor_ct_amounts], -// |item| item.0, -// Zero::zero(), -// |item, accumulator| accumulator + item.1, -// ); - -// for (account, amount) in all_ct_expectations { -// let minted = -// inst.execute(|| ::ContributionTokenCurrency::balance(project_id, account)); -// assert_eq!(minted, amount, "Account: {}", account); -// } -// } - -// #[test] -// fn multiplier_gets_correct_vesting_duration() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = vec![ -// BidParams::new(BIDDER_1, 325_000 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT), -// BidParams::new(BIDDER_2, 75_000 * ASSET_UNIT, 2u8, AcceptedFundingAsset::USDT), -// BidParams::new(BIDDER_3, 50_000 * ASSET_UNIT, 3u8, AcceptedFundingAsset::USDT), -// ]; -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids, -// community_contributions, -// remainder_contributions, -// ); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - -// inst.advance_time(10u64).unwrap(); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - -// let mut stored_bids = -// inst.execute(|| Bids::::iter_prefix_values((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, -// FixedU128::from_rational(2167, 1000).saturating_mul_int(one_week_in_blocks as u64) -// ); -// assert_eq!( -// stored_bids[2].plmc_vesting_info.unwrap().duration, -// FixedU128::from_rational(4334, 1000).saturating_mul_int(one_week_in_blocks as u64) -// ); -// } - -// #[test] -// fn funding_assets_are_paid_manually_to_issuer() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids, -// community_contributions, -// remainder_contributions, -// ); - -// let final_winning_bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) -// .collect::>() -// }); -// let final_bid_payouts = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) -// .map(|bid| { -// UserToForeignAssets::new( -// bid.bidder, -// bid.funding_asset_amount_locked, -// bid.funding_asset.to_assethub_id(), -// ) -// }) -// .collect::>>() -// }); -// let final_contributions = -// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); -// let final_contribution_payouts = inst.execute(|| { -// Contributions::::iter_prefix_values((project_id,)) -// .map(|contribution| { -// UserToForeignAssets::new( -// contribution.contributor, -// contribution.funding_asset_amount, -// contribution.funding_asset.to_assethub_id(), -// ) -// }) -// .collect::>>() -// }); - -// let total_expected_bid_payout = -// final_bid_payouts.iter().map(|bid| bid.asset_amount).sum::>(); -// let total_expected_contribution_payout = final_contribution_payouts -// .iter() -// .map(|contribution| contribution.asset_amount) -// .sum::>(); - -// let prev_issuer_funding_balance = -// inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - -// let prev_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( -// final_bid_payouts[0].asset_id, -// vec![Pallet::::fund_account_id(project_id)], -// )[0] -// .asset_amount; - -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// assert_eq!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Success(CleanerState::Initialized(PhantomData)) -// ); -// for bid in final_winning_bids { -// inst.execute(|| { -// Pallet::::payout_bid_funds_for( -// RuntimeOrigin::signed(issuer), -// project_id, -// bid.bidder, -// bid.id, -// ) -// }) -// .unwrap(); -// } -// for contribution in final_contributions { -// inst.execute(|| { -// Pallet::::payout_contribution_funds_for( -// RuntimeOrigin::signed(issuer), -// project_id, -// contribution.contributor, -// contribution.id, -// ) -// }) -// .unwrap(); -// } -// let post_issuer_funding_balance = -// inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - -// let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( -// final_bid_payouts[0].asset_id, -// vec![Pallet::::fund_account_id(project_id)], -// )[0] -// .asset_amount; -// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; -// let project_pot_funding_delta = prev_project_pot_funding_balance - post_project_pot_funding_balance; - -// assert_eq!(issuer_funding_delta - total_expected_bid_payout, total_expected_contribution_payout); -// assert_eq!(issuer_funding_delta, project_pot_funding_delta); - -// assert_eq!(post_project_pot_funding_balance, 0u128); -// } - -// #[test] -// fn funding_assets_are_paid_automatically_to_issuer() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids, -// community_contributions, -// remainder_contributions, -// ); - -// let final_bid_payouts = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) -// .map(|bid| { -// UserToForeignAssets::new( -// bid.bidder, -// bid.funding_asset_amount_locked, -// bid.funding_asset.to_assethub_id(), -// ) -// }) -// .collect::>>() -// }); -// let final_contribution_payouts = inst.execute(|| { -// Contributions::::iter_prefix_values((project_id,)) -// .map(|contribution| { -// UserToForeignAssets::new( -// contribution.contributor, -// contribution.funding_asset_amount, -// contribution.funding_asset.to_assethub_id(), -// ) -// }) -// .collect::>>() -// }); - -// let total_expected_bid_payout = -// final_bid_payouts.iter().map(|bid| bid.asset_amount).sum::>(); -// let total_expected_contribution_payout = final_contribution_payouts -// .iter() -// .map(|contribution| contribution.asset_amount) -// .sum::>(); - -// let prev_issuer_funding_balance = -// inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - -// let prev_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( -// final_bid_payouts[0].asset_id, -// vec![Pallet::::fund_account_id(project_id)], -// )[0] -// .asset_amount; - -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// assert_eq!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Success(CleanerState::Initialized(PhantomData)) -// ); -// inst.advance_time(1u64).unwrap(); -// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - -// let post_issuer_funding_balance = -// inst.get_free_foreign_asset_balances_for(final_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; - -// let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( -// final_bid_payouts[0].asset_id, -// vec![Pallet::::fund_account_id(project_id)], -// )[0] -// .asset_amount; -// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; -// let project_pot_funding_delta = prev_project_pot_funding_balance - post_project_pot_funding_balance; - -// assert_eq!(issuer_funding_delta - total_expected_bid_payout, total_expected_contribution_payout); -// assert_eq!(issuer_funding_delta, project_pot_funding_delta); - -// assert_eq!(post_project_pot_funding_balance, 0u128); -// } - -// #[test] -// fn funding_assets_are_released_automatically_on_funding_fail() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); - -// let auction_allocation = -// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int(auction_allocation), -// project_metadata.minimum_price, -// default_weights(), -// default_bidders(), -// default_bidder_multipliers(), -// ); -// let community_contributions = MockInstantiator::generate_contributions_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int( -// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, -// ), -// project_metadata.minimum_price, -// default_weights(), -// default_community_contributors(), -// default_community_contributor_multipliers(), -// ); -// let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int( -// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, -// ), -// project_metadata.minimum_price, -// default_weights(), -// default_remainder_contributors(), -// default_remainder_contributor_multipliers(), -// ); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids, -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); - -// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); -// let expected_bid_payouts = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .map(|bid| { -// UserToForeignAssets::::new( -// bid.bidder, -// bid.funding_asset_amount_locked, -// bid.funding_asset.to_assethub_id(), -// ) -// }) -// .sorted_by_key(|bid| bid.account) -// .collect::>>() -// }); -// let expected_community_contribution_payouts = -// MockInstantiator::calculate_contributed_funding_asset_spent(community_contributions, final_price); -// let expected_remainder_contribution_payouts = -// MockInstantiator::calculate_contributed_funding_asset_spent(remainder_contributions, final_price); -// let all_expected_payouts = MockInstantiator::generic_map_operation( -// vec![ -// expected_bid_payouts.clone(), -// expected_community_contribution_payouts, -// expected_remainder_contribution_payouts, -// ], -// MergeOperation::Add, -// ); - -// let prev_issuer_funding_balance = -// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; -// let all_participants = all_expected_payouts.accounts(); -// let prev_participants_funding_balances = -// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants.clone()); - -// call_and_is_ok!( -// inst, -// Pallet::::decide_project_outcome( -// RuntimeOrigin::signed(issuer), -// project_id, -// FundingOutcomeDecision::RejectFunding -// ) -// ); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// inst.advance_time(10).unwrap(); -// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Failure(CleanerState::Finished(PhantomData))); - -// let post_issuer_funding_balance = -// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; -// let post_participants_funding_balances = -// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants); -// let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( -// expected_bid_payouts[0].asset_id, -// vec![Pallet::::fund_account_id(project_id)], -// )[0] -// .asset_amount; - -// let all_participants_funding_delta = MockInstantiator::generic_map_operation( -// vec![prev_participants_funding_balances, post_participants_funding_balances], -// MergeOperation::Add, -// ); - -// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - -// assert_eq!(issuer_funding_delta, 0); -// assert_eq!(post_project_pot_funding_balance, 0u128); -// assert_eq!(all_expected_payouts, all_participants_funding_delta); -// } - -// #[test] -// fn funding_assets_are_released_manually_on_funding_fail() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let auction_allocation = -// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int(auction_allocation), -// project_metadata.minimum_price, -// default_weights(), -// default_bidders(), -// default_bidder_multipliers(), -// ); -// let community_contributions = MockInstantiator::generate_contributions_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int( -// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, -// ), -// project_metadata.minimum_price, -// default_weights(), -// default_community_contributors(), -// default_community_contributor_multipliers(), -// ); -// let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int( -// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, -// ), -// project_metadata.minimum_price, -// default_weights(), -// default_remainder_contributors(), -// default_remainder_contributor_multipliers(), -// ); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids, -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); -// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); -// let expected_bid_payouts = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .map(|bid| { -// UserToForeignAssets::::new( -// bid.bidder, -// bid.funding_asset_amount_locked, -// bid.funding_asset.to_assethub_id(), -// ) -// }) -// .sorted_by_key(|item| item.account) -// .collect::>>() -// }); -// let expected_community_contribution_payouts = -// MockInstantiator::calculate_contributed_funding_asset_spent(community_contributions, final_price); -// let expected_remainder_contribution_payouts = -// MockInstantiator::calculate_contributed_funding_asset_spent(remainder_contributions, final_price); -// let all_expected_payouts = MockInstantiator::generic_map_operation( -// vec![ -// expected_bid_payouts.clone(), -// expected_community_contribution_payouts, -// expected_remainder_contribution_payouts, -// ], -// MergeOperation::Add, -// ); - -// let prev_issuer_funding_balance = -// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; -// let all_participants = all_expected_payouts.accounts(); -// let prev_participants_funding_balances = -// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants.clone()); - -// call_and_is_ok!( -// inst, -// Pallet::::decide_project_outcome( -// RuntimeOrigin::signed(issuer), -// project_id, -// FundingOutcomeDecision::RejectFunding -// ) -// ); - -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - -// let stored_bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) -// .collect::>() -// }); -// let stored_contributions = -// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - -// for bid in stored_bids { -// call_and_is_ok!( -// inst, -// Pallet::::release_bid_funds_for( -// RuntimeOrigin::signed(issuer), -// project_id, -// bid.bidder, -// bid.id, -// ) -// ) -// } - -// for contribution in stored_contributions { -// call_and_is_ok!( -// inst, -// Pallet::::release_contribution_funds_for( -// RuntimeOrigin::signed(issuer), -// project_id, -// contribution.contributor, -// contribution.id, -// ) -// ) -// } - -// let post_issuer_funding_balance = -// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, vec![issuer])[0].asset_amount; -// let post_participants_funding_balances = -// inst.get_free_foreign_asset_balances_for(expected_bid_payouts[0].asset_id, all_participants); -// let post_project_pot_funding_balance = inst.get_free_foreign_asset_balances_for( -// expected_bid_payouts[0].asset_id, -// vec![Pallet::::fund_account_id(project_id)], -// )[0] -// .asset_amount; - -// let all_participants_funding_delta = MockInstantiator::generic_map_operation( -// vec![prev_participants_funding_balances, post_participants_funding_balances], -// MergeOperation::Add, -// ); - -// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - -// assert_eq!(issuer_funding_delta, 0); -// assert_eq!(post_project_pot_funding_balance, 0u128); -// assert_eq!(all_expected_payouts, all_participants_funding_delta); -// } - -// #[test] -// fn plmc_bonded_is_returned_automatically_on_funding_fail() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let auction_allocation = -// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int(auction_allocation), -// project_metadata.minimum_price, -// default_weights(), -// default_bidders(), -// default_bidder_multipliers(), -// ); -// let community_contributions = MockInstantiator::generate_contributions_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int( -// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, -// ), -// project_metadata.minimum_price, -// default_weights(), -// default_community_contributors(), -// default_community_contributor_multipliers(), -// ); -// let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int( -// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, -// ), -// project_metadata.minimum_price, -// default_weights(), -// default_remainder_contributors(), -// default_remainder_contributor_multipliers(), -// ); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations.clone(), -// bids.clone(), -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); -// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - -// let expected_evaluators_and_contributors_payouts = -// MockInstantiator::calculate_total_plmc_locked_from_evaluations_and_remainder_contributions( -// evaluations.clone(), -// remainder_contributions.clone(), -// final_price, -// true, -// ); -// let expected_bid_payouts = -// MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, final_price); -// let expected_community_contribution_payouts = -// MockInstantiator::calculate_contributed_plmc_spent(community_contributions.clone(), final_price); -// let all_expected_payouts = MockInstantiator::generic_map_operation( -// vec![ -// expected_evaluators_and_contributors_payouts.clone(), -// expected_bid_payouts, -// expected_community_contribution_payouts, -// ], -// MergeOperation::Add, -// ); -// println!("all expected payouts {:?}", all_expected_payouts); -// for payout in all_expected_payouts.clone() { -// let evaluation_hold = inst.execute(|| { -// <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( -// &HoldReason::Evaluation(project_id).into(), -// &payout.account, -// ) -// }); -// let participation_hold = inst.execute(|| { -// <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( -// &HoldReason::Participation(project_id).into(), -// &payout.account, -// ) -// }); -// println!("account {:?} has evaluation hold {:?}", payout.account, evaluation_hold); -// println!("account {:?} has participation hold {:?}", payout.account, participation_hold); -// } -// let deposit_required = <::ContributionTokenCurrency as AccountTouch< -// ProjectId, -// AccountIdOf, -// >>::deposit_required(project_id); -// let all_expected_payouts = all_expected_payouts -// .into_iter() -// .map(|UserToPLMCBalance { account, plmc_amount }| { -// UserToPLMCBalance::new(account, plmc_amount + deposit_required) -// }) -// .collect::>(); - -// let prev_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; - -// let all_participants = all_expected_payouts.accounts(); -// let prev_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants.clone()); - -// call_and_is_ok!( -// inst, -// Pallet::::decide_project_outcome( -// RuntimeOrigin::signed(issuer), -// project_id, -// FundingOutcomeDecision::RejectFunding -// ) -// ); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// inst.advance_time(10).unwrap(); -// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Failure(CleanerState::Finished(PhantomData))); - -// let post_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; -// let post_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants); - -// let all_participants_plmc_deltas = MockInstantiator::generic_map_operation( -// vec![post_participants_plmc_balances, prev_participants_plmc_balances], -// MergeOperation::Subtract, -// ); - -// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; - -// assert_eq!(issuer_funding_delta, 0); -// assert_eq!(all_participants_plmc_deltas, all_expected_payouts); -// } - -// #[test] -// fn plmc_bonded_is_returned_manually_on_funding_fail() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let auction_allocation = -// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; -// let evaluations = default_evaluations(); -// let bids = MockInstantiator::generate_bids_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int(auction_allocation), -// project_metadata.minimum_price, -// default_weights(), -// default_bidders(), -// default_bidder_multipliers(), -// ); -// let community_contributions = MockInstantiator::generate_contributions_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int( -// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, -// ), -// project_metadata.minimum_price, -// default_weights(), -// default_community_contributors(), -// default_community_contributor_multipliers(), -// ); -// let remainder_contributions = MockInstantiator::generate_contributions_from_total_usd( -// project_metadata.minimum_price.saturating_mul_int( -// Percent::from_percent(50u8) * (project_metadata.total_allocation_size - auction_allocation) / 2, -// ), -// project_metadata.minimum_price, -// default_weights(), -// default_remainder_contributors(), -// default_remainder_contributor_multipliers(), -// ); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations.clone(), -// bids.clone(), -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); -// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - -// let expected_evaluators_and_contributors_payouts = -// MockInstantiator::calculate_total_plmc_locked_from_evaluations_and_remainder_contributions( -// evaluations.clone(), -// remainder_contributions.clone(), -// final_price, -// true, -// ); -// let expected_bid_payouts = -// MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, final_price); -// let expected_community_contribution_payouts = -// MockInstantiator::calculate_contributed_plmc_spent(community_contributions.clone(), final_price); -// let all_expected_payouts = MockInstantiator::generic_map_operation( -// vec![ -// expected_evaluators_and_contributors_payouts.clone(), -// expected_bid_payouts, -// expected_community_contribution_payouts, -// ], -// MergeOperation::Add, -// ); -// println!("all expected payouts {:?}", all_expected_payouts); -// for payout in all_expected_payouts.clone() { -// let evaluation_hold = inst.execute(|| { -// <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( -// &HoldReason::Evaluation(project_id).into(), -// &payout.account, -// ) -// }); -// let participation_hold = inst.execute(|| { -// <::NativeCurrency as fungible::InspectHold>>::balance_on_hold( -// &HoldReason::Participation(project_id).into(), -// &payout.account, -// ) -// }); -// println!("account {:?} has evaluation hold {:?}", payout.account, evaluation_hold); -// println!("account {:?} has participation hold {:?}", payout.account, participation_hold); -// } -// let _deposit_required = <::ContributionTokenCurrency as AccountTouch< -// ProjectId, -// AccountIdOf, -// >>::deposit_required(project_id); - -// let prev_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; -// let all_participants = all_expected_payouts.accounts(); -// let prev_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants.clone()); - -// call_and_is_ok!( -// inst, -// Pallet::::decide_project_outcome( -// RuntimeOrigin::signed(issuer), -// project_id, -// FundingOutcomeDecision::RejectFunding -// ) -// ); -// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); -// assert_eq!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Failure(CleanerState::Initialized(PhantomData)) -// ); - -// let stored_evaluations = -// inst.execute(|| Evaluations::::iter_prefix_values((project_id,)).collect::>()); -// let stored_bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..))) -// .collect::>() -// }); -// let stored_contributions = -// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - -// for evaluation in stored_evaluations { -// call_and_is_ok!( -// inst, -// Pallet::::evaluation_slash_for( -// RuntimeOrigin::signed(evaluation.evaluator), -// project_id, -// evaluation.evaluator, -// evaluation.id, -// ), -// Pallet::::evaluation_unbond_for( -// RuntimeOrigin::signed(evaluation.evaluator), -// project_id, -// evaluation.evaluator, -// evaluation.id, -// ) -// ) -// } - -// for bid in stored_bids { -// call_and_is_ok!( -// inst, -// Pallet::::release_bid_funds_for( -// RuntimeOrigin::signed(issuer), -// project_id, -// bid.bidder, -// bid.id, -// ), -// Pallet::::bid_unbond_for( -// RuntimeOrigin::signed(bid.bidder), -// project_id, -// bid.bidder, -// bid.id, -// ) -// ) -// } - -// for contribution in stored_contributions { -// call_and_is_ok!( -// inst, -// Pallet::::release_contribution_funds_for( -// RuntimeOrigin::signed(issuer), -// project_id, -// contribution.contributor, -// contribution.id, -// ), -// Pallet::::contribution_unbond_for( -// RuntimeOrigin::signed(contribution.contributor), -// project_id, -// contribution.contributor, -// contribution.id, -// ) -// ) -// } - -// let post_issuer_funding_balance = inst.get_free_plmc_balances_for(vec![issuer])[0].plmc_amount; -// let post_participants_plmc_balances = inst.get_free_plmc_balances_for(all_participants); - -// let all_participants_plmc_deltas = MockInstantiator::generic_map_operation( -// vec![post_participants_plmc_balances, prev_participants_plmc_balances], -// MergeOperation::Subtract, -// ); - -// let issuer_funding_delta = post_issuer_funding_balance - prev_issuer_funding_balance; -// assert_eq!( -// inst.get_project_details(project_id).cleanup, -// Cleaner::Failure(CleanerState::Initialized(PhantomData)) -// ); -// assert_eq!(issuer_funding_delta, 0); -// assert_eq!(all_participants_plmc_deltas, all_expected_payouts); -// } - -// // i.e consumer increase bug fixed with touch on pallet-assets -// #[test] -// fn no_limit_on_project_contributions_per_user() { -// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - -// let project = |x| TestProjectParams { -// expected_state: ProjectStatus::FundingSuccessful, -// metadata: default_project_metadata(x, ISSUER), -// evaluations: default_evaluations(), -// bids: default_bids(), -// community_contributions: default_community_buys(), -// remainder_contributions: default_remainder_buys(), -// issuer: ISSUER, -// }; -// let projects = (0..20).into_iter().map(|x| project(x)).collect_vec(); -// async_features::create_multiple_projects_at(inst, projects); -// } - -// #[test] -// fn evaluation_plmc_unbonded_after_funding_success() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let evaluations = default_evaluations(); -// let evaluators = evaluations.accounts(); - -// let project_id = inst.create_remainder_contributing_project( -// default_project_metadata(inst.get_new_nonce(), ISSUER), -// ISSUER, -// evaluations.clone(), -// default_bids(), -// default_community_buys(), -// ); - -// let prev_reserved_plmc = -// inst.get_reserved_plmc_balances_for(evaluators.clone(), HoldReason::Evaluation(project_id).into()); - -// let prev_free_plmc = inst.get_free_plmc_balances_for(evaluators.clone()); - -// inst.finish_funding(project_id).unwrap(); -// inst.advance_time(::ManualAcceptanceDuration::get() + 1).unwrap(); -// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); -// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); -// assert_eq!(inst.get_project_details(project_id).cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); -// inst.advance_time(10).unwrap(); -// let post_unbond_amounts: Vec> = prev_reserved_plmc -// .iter() -// .map(|UserToPLMCBalance { account, .. }| UserToPLMCBalance::new(*account, Zero::zero())) -// .collect(); - -// inst.do_reserved_plmc_assertions(post_unbond_amounts.clone(), HoldReason::Evaluation(project_id).into()); -// inst.do_reserved_plmc_assertions(post_unbond_amounts, HoldReason::Participation(project_id).into()); - -// let post_free_plmc = inst.get_free_plmc_balances_for(evaluators); - -// let increased_amounts = -// MockInstantiator::generic_map_operation(vec![post_free_plmc, prev_free_plmc], MergeOperation::Subtract); - -// assert_eq!(increased_amounts, MockInstantiator::calculate_evaluation_plmc_spent(evaluations)) -// } - -// #[test] -// fn plmc_vesting_schedule_starts_automatically() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let mut bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids.clone(), -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); - -// let price = inst.get_project_details(project_id).weighted_average_price.unwrap(); -// let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); -// bids = -// stored_bids.into_iter().map(|bid| BidParams::new_with_defaults(bid.bidder, bid.final_ct_amount)).collect(); -// let auction_locked_plmc = MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, price); -// let community_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(community_contributions, price); -// let remainder_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(remainder_contributions, price); -// let all_plmc_locks = MockInstantiator::generic_map_operation( -// vec![auction_locked_plmc, community_locked_plmc, remainder_locked_plmc], -// MergeOperation::Add, -// ); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - -// inst.advance_time(10u64).unwrap(); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - -// for UserToPLMCBalance { account, plmc_amount } in all_plmc_locks { -// let schedule = inst.execute(|| { -// ::Vesting::total_scheduled_amount( -// &account, -// HoldReason::Participation(project_id).into(), -// ) -// }); - -// assert_eq!(schedule.unwrap(), plmc_amount); -// } -// } - -// #[test] -// fn plmc_vesting_schedule_starts_manually() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids.clone(), -// community_contributions.clone(), -// remainder_contributions.clone(), -// ); - -// let price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Initialized(PhantomData))); - -// let stored_successful_bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) -// .collect::>() -// }); -// let stored_contributions = -// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - -// for bid in stored_successful_bids.clone() { -// call_and_is_ok!( -// inst, -// Pallet::::do_start_bid_vesting_schedule_for(&bid.bidder, project_id, &bid.bidder, bid.id,) -// ); -// } -// for contribution in stored_contributions { -// call_and_is_ok!( -// inst, -// Pallet::::do_start_contribution_vesting_schedule_for( -// &contribution.contributor, -// project_id, -// &contribution.contributor, -// contribution.id, -// ) -// ); -// } - -// let auction_locked_plmc = MockInstantiator::calculate_auction_plmc_charged_with_given_price(&bids, price); -// let community_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(community_contributions, price); -// let remainder_locked_plmc = MockInstantiator::calculate_contributed_plmc_spent(remainder_contributions, price); -// let all_plmc_locks = MockInstantiator::generic_map_operation( -// vec![auction_locked_plmc, community_locked_plmc, remainder_locked_plmc], -// MergeOperation::Add, -// ); - -// for UserToPLMCBalance { account, plmc_amount } in all_plmc_locks { -// let schedule = inst.execute(|| { -// ::Vesting::total_scheduled_amount( -// &account, -// HoldReason::Participation(project_id).into(), -// ) -// }); - -// assert_eq!(schedule.unwrap(), plmc_amount); -// } -// } - -// #[test] -// fn plmc_vesting_full_amount() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids, -// community_contributions, -// remainder_contributions, -// ); -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - -// inst.advance_time(10u64).unwrap(); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); - -// let stored_successful_bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) -// .collect::>() -// }); - -// let stored_contributions = -// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - -// let total_bid_plmc_in_vesting: Vec> = stored_successful_bids -// .iter() -// .map(|bid| (bid.bidder, bid.plmc_vesting_info.unwrap().total_amount).into()) -// .collect_vec(); - -// let total_contribution_plmc_in_vesting: Vec> = stored_contributions -// .iter() -// .map(|contribution| (contribution.contributor, contribution.plmc_vesting_info.unwrap().total_amount).into()) -// .collect_vec(); - -// let total_participant_plmc_in_vesting = MockInstantiator::generic_map_operation( -// vec![total_bid_plmc_in_vesting, total_contribution_plmc_in_vesting], -// MergeOperation::Add, -// ); - -// inst.advance_time((10 * DAYS).into()).unwrap(); - -// for UserToPLMCBalance { account, plmc_amount } in total_participant_plmc_in_vesting { -// let prev_free_balance = inst.execute(|| ::NativeCurrency::balance(&account)); - -// inst.execute(|| Pallet::::do_vest_plmc_for(account, project_id, account)).unwrap(); - -// let post_free_balance = inst.execute(|| ::NativeCurrency::balance(&account)); -// assert_eq!(plmc_amount, post_free_balance - prev_free_balance); -// } -// } - -// #[test] -// fn plmc_vesting_partial_amount() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); -// let issuer = ISSUER; -// let project_metadata = default_project_metadata(inst.get_new_nonce(), issuer); -// let evaluations = default_evaluations(); -// let bids = default_bids(); -// let community_contributions = default_community_buys(); -// let remainder_contributions = default_remainder_buys(); - -// let project_id = inst.create_finished_project( -// project_metadata, -// issuer, -// evaluations, -// bids, -// community_contributions, -// remainder_contributions, -// ); - -// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); -// inst.advance_time(15u64).unwrap(); -// let details = inst.get_project_details(project_id); -// assert_eq!(details.cleanup, Cleaner::Success(CleanerState::Finished(PhantomData))); -// let vest_start_block = details.funding_end_block.unwrap(); -// let stored_successful_bids = inst.execute(|| { -// Bids::::iter_prefix_values((project_id,)) -// .filter(|bid| matches!(bid.status, BidStatus::Rejected(_)).not()) -// .collect::>() -// }); -// let stored_contributions = -// inst.execute(|| Contributions::::iter_prefix_values((project_id,)).collect::>()); - -// let bidder_vesting = -// stored_successful_bids.iter().map(|bid| (bid.bidder, bid.plmc_vesting_info.unwrap())).collect_vec(); -// let contributor_vesting = stored_contributions -// .iter() -// .map(|contribution| (contribution.contributor, contribution.plmc_vesting_info.unwrap())) -// .collect_vec(); - -// let participant_vesting_infos: Vec<(AccountIdOf, Vec>)> = -// MockInstantiator::generic_map_merge_reduce( -// vec![bidder_vesting, contributor_vesting], -// |map| map.0, -// Vec::new(), -// |map, mut vestings| { -// vestings.push(map.1); -// vestings -// }, -// ); - -// let now = inst.current_block(); -// for (participant, vesting_infos) in participant_vesting_infos { -// let vested_amount = vesting_infos.into_iter().fold(0u128, |acc, vesting_info| { -// acc + vesting_info.amount_per_block * min(vesting_info.duration, now - vest_start_block) as u128 -// }); - -// let prev_free_balance = inst.execute(|| ::NativeCurrency::balance(&participant)); - -// inst.execute(|| Pallet::::do_vest_plmc_for(participant, project_id, participant)).unwrap(); - -// let post_free_balance = inst.execute(|| ::NativeCurrency::balance(&participant)); -// assert_eq!(vested_amount, post_free_balance - prev_free_balance); -// } -// } - -// #[test] -// fn ct_treasury_mints() { -// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - -// let treasury_account = ::ContributionTreasury::get(); - -// let project_metadata = ProjectMetadataOf:: { -// token_information: default_token_information(), -// mainnet_token_max_supply: 8_000_000 * ASSET_UNIT, -// total_allocation_size: 1_000_000 * ASSET_UNIT, -// auction_round_allocation_percentage: Percent::from_percent(50u8), -// minimum_price: PriceOf::::from_float(10.0), -// bidding_ticket_sizes: BiddingTicketSizes { -// professional: TicketSize::new(Some(5000 * US_DOLLAR), None), -// institutional: TicketSize::new(Some(5000 * US_DOLLAR), None), -// phantom: Default::default(), -// }, -// contributing_ticket_sizes: ContributingTicketSizes { -// retail: TicketSize::new(None, None), -// professional: TicketSize::new(None, None), -// institutional: TicketSize::new(None, None), -// phantom: Default::default(), -// }, -// participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), -// funding_destination_account: ISSUER, -// offchain_information_hash: Some(hashed(METADATA)), -// }; -// let mut counter: u8 = 0u8; -// let mut with_different_metadata = |mut project: ProjectMetadataOf| { -// let mut binding = project.offchain_information_hash.unwrap(); -// let h256_bytes = binding.as_fixed_bytes_mut(); -// h256_bytes[0] = counter; -// counter += 1u8; -// project.offchain_information_hash = Some(binding); -// project -// }; - -// let price = project_metadata.minimum_price; - -// // Failed project has no mints on the treasury -// let project_20_percent = inst.create_finished_project( -// with_different_metadata(project_metadata.clone()), -// ISSUER, -// default_evaluations(), -// default_bids_from_ct_percent(10), -// default_community_contributions_from_ct_percent(10), -// vec![], -// ); -// inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); -// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); -// let ct_balance = inst.execute(|| { -// ::ContributionTokenCurrency::balance(project_20_percent, treasury_account) -// }); -// assert_eq!(ct_balance, 0); - -// // 50% funded project can have mints on the treasury if issuer accepts or enough time passes for automatic acceptance -// let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; -// let fee_8_percent = Percent::from_percent(8) * 4_000_000 * US_DOLLAR; -// let fee_6_percent = Percent::from_percent(6) * 0 * US_DOLLAR; -// let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; -// let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); - -// let project_50_percent = inst.create_finished_project( -// with_different_metadata(project_metadata.clone()), -// ISSUER, -// default_evaluations(), -// default_bids_from_ct_percent(25), -// default_community_contributions_from_ct_percent(20), -// default_remainder_contributions_from_ct_percent(5), -// ); -// inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); -// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); -// let ct_balance = inst.execute(|| { -// ::ContributionTokenCurrency::balance(project_50_percent, treasury_account) -// }); -// let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; -// let expected_long_term_holder_bonus_minted = Percent::from_percent(50) * total_ct_fee; -// assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); - -// // 80% funded project can have mints on the treasury if issuer accepts or enough time passes for automatic acceptance -// let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; -// let fee_8_percent = Percent::from_percent(8) * 5_000_000 * US_DOLLAR; -// let fee_6_percent = Percent::from_percent(6) * 2_000_000 * US_DOLLAR; -// let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; -// let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); - -// let project_80_percent = inst.create_finished_project( -// with_different_metadata(project_metadata.clone()), -// ISSUER, -// default_evaluations(), -// default_bids_from_ct_percent(40), -// default_community_contributions_from_ct_percent(30), -// default_remainder_contributions_from_ct_percent(10), -// ); -// inst.advance_time(::ManualAcceptanceDuration::get()).unwrap(); -// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); -// let ct_balance = inst.execute(|| { -// ::ContributionTokenCurrency::balance(project_80_percent, treasury_account) -// }); -// let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; -// let expected_long_term_holder_bonus_minted = Percent::from_percent(50) * total_ct_fee; -// assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); - -// // 98% funded project always has mints on the treasury -// let fee_10_percent = Percent::from_percent(10) * 1_000_000 * US_DOLLAR; -// let fee_8_percent = Percent::from_percent(8) * 5_000_000 * US_DOLLAR; -// let fee_6_percent = Percent::from_percent(6) * 3_800_000 * US_DOLLAR; -// let total_usd_fee = fee_10_percent + fee_8_percent + fee_6_percent; -// let total_ct_fee = price.reciprocal().unwrap().saturating_mul_int(total_usd_fee); - -// let project_98_percent = inst.create_finished_project( -// with_different_metadata(project_metadata.clone()), -// ISSUER, -// default_evaluations(), -// default_bids_from_ct_percent(49), -// default_community_contributions_from_ct_percent(39), -// default_remainder_contributions_from_ct_percent(10), -// ); -// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); -// let ct_balance = inst.execute(|| { -// ::ContributionTokenCurrency::balance(project_98_percent, treasury_account) -// }); -// let expected_liquidity_pool_minted = Percent::from_percent(50) * total_ct_fee; -// let lthb_percent = Perquintill::from_percent(20) + Perquintill::from_percent(30) * Perquintill::from_percent(2); -// let expected_long_term_holder_bonus_minted = lthb_percent * total_ct_fee; -// assert_eq!(ct_balance, expected_liquidity_pool_minted + expected_long_term_holder_bonus_minted); - -// // Test the touch on the treasury ct account by the issuer. -// // We create more CT accounts that the account can use provider references for, -// // so if it succeeds, then it means the touch was successful. -// let consumer_limit: u32 = ::MaxConsumers::get(); - -// // we want to test ct mints on treasury of 1 over the consumer limit, -// // and we already minted 3 contribution tokens on previous tests. -// for i in 0..consumer_limit + 1u32 - 3u32 { -// let _project_98_percent = inst.create_finished_project( -// with_different_metadata(project_metadata.clone()), -// ISSUER + i + 1000, -// default_evaluations(), -// default_bids_from_ct_percent(49), -// default_community_contributions_from_ct_percent(39), -// default_remainder_contributions_from_ct_percent(10), -// ); -// inst.advance_time(::SuccessToSettlementTime::get() + 1).unwrap(); -// } -// } -// } +mod funding_end { + use super::*; + + #[test] + fn automatic_fail_less_eq_33_percent() { + for funding_percent in (1..=33).step_by(5) { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); + let min_price = project_metadata.minimum_price; + let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * + (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); + let evaluations = default_evaluations(); + let bids = MockInstantiator::generate_bids_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + vec![100u8], + vec![BIDDER_1], + vec![10u8], + ); + let contributions = MockInstantiator::generate_contributions_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_community_contributors(), + default_multipliers(), + ); + let project_id = + inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); + } + } + + #[test] + fn automatic_success_bigger_eq_90_percent() { + for funding_percent in (90..=100).step_by(2) { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); + let min_price = project_metadata.minimum_price; + let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * + (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); + let evaluations = default_evaluations(); + let bids = MockInstantiator::generate_bids_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_bidders(), + default_multipliers(), + ); + let contributions = MockInstantiator::generate_contributions_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_community_contributors(), + default_multipliers(), + ); + let project_id = + inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); + } + } + + #[test] + fn manual_outcome_above33_to_below90() { + for funding_percent in (34..90).step_by(5) { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); + let min_price = project_metadata.minimum_price; + let twenty_percent_funding_usd = Perquintill::from_percent(funding_percent) * + (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); + let evaluations = default_evaluations(); + let bids = MockInstantiator::generate_bids_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_bidders(), + default_multipliers(), + ); + let contributions = MockInstantiator::generate_contributions_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_community_contributors(), + default_multipliers(), + ); + let project_id = + inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + } + } + + #[test] + fn manual_acceptance() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); + let min_price = project_metadata.minimum_price; + let twenty_percent_funding_usd = Perquintill::from_percent(55) * + (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); + let evaluations = default_evaluations(); + let bids = MockInstantiator::generate_bids_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_bidders(), + default_multipliers(), + ); + let contributions = MockInstantiator::generate_contributions_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_community_contributors(), + default_multipliers(), + ); + let project_id = + inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + + let project_id = project_id; + inst.execute(|| { + PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::AcceptFunding) + }) + .unwrap(); + + inst.advance_time(1u64).unwrap(); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); + inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + + inst.test_ct_created_for(project_id); + + inst.settle_project(project_id).unwrap(); + } + + #[test] + fn manual_rejection() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); + let min_price = project_metadata.minimum_price; + let twenty_percent_funding_usd = Perquintill::from_percent(55) * + (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); + let evaluations = default_evaluations(); + let bids = MockInstantiator::generate_bids_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_bidders(), + default_multipliers(), + ); + let contributions = MockInstantiator::generate_contributions_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_community_contributors(), + default_multipliers(), + ); + let project_id = + inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + + let project_id = project_id; + inst.execute(|| { + PolimecFunding::do_decide_project_outcome(ISSUER, project_id, FundingOutcomeDecision::RejectFunding) + }) + .unwrap(); + + inst.advance_time(1u64).unwrap(); + + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); + inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + + inst.test_ct_not_created_for(project_id); + + inst.settle_project(project_id).unwrap(); + } + + #[test] + fn automatic_acceptance_on_manual_decision_after_time_delta() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER); + let min_price = project_metadata.minimum_price; + let twenty_percent_funding_usd = Perquintill::from_percent(55) * + (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); + let evaluations = default_evaluations(); + let bids = MockInstantiator::generate_bids_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_bidders(), + default_multipliers(), + ); + let contributions = MockInstantiator::generate_contributions_from_total_usd( + Percent::from_percent(50u8) * twenty_percent_funding_usd, + min_price, + default_weights(), + default_community_contributors(), + default_multipliers(), + ); + let project_id = + inst.create_finished_project(project_metadata, ISSUER, evaluations, bids, contributions, vec![]); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AwaitingProjectDecision); + + let project_id = project_id; + inst.advance_time(1u64 + ::ManualAcceptanceDuration::get()).unwrap(); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); + inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); + + inst.test_ct_created_for(project_id); + + inst.settle_project(project_id).unwrap(); + } +} // only functionalities related to the CT Migration mod ct_migration { use super::*; use frame_support::assert_err; - #[test] - fn max_number_of_migrations() { - dbg!(Pallet::::migrations_per_xcm_message_allowed()); - } - #[test] fn para_id_for_project_can_be_set_by_issuer() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); @@ -5705,12 +3800,6 @@ mod ct_migration { let project_details = inst.get_project_details(project_id); assert_eq!(project_details.parachain_id, None); } - - #[test] - fn check_migrations_per_xcm() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - inst.execute(|| dbg!(Pallet::::migrations_per_xcm_message_allowed())); - } } // check that functions created to facilitate testing return the expected results diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index 76a7608b3..1f79cffd7 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -774,13 +774,4 @@ pub mod inner_types { pub project_id: ProjectId, pub migration_origins: MigrationOrigins, } - - - #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub struct MaxMigrationsPerXcm(PhantomData); - impl Get for MaxMigrationsPerXcm { - fn get() -> u32 { - crate::Pallet::::migrations_per_xcm_message_allowed() - } - } } \ No newline at end of file diff --git a/polimec-common/common/src/lib.rs b/polimec-common/common/src/lib.rs index 87b936b49..80512c0b5 100644 --- a/polimec-common/common/src/lib.rs +++ b/polimec-common/common/src/lib.rs @@ -185,6 +185,14 @@ pub mod migration_types { Self(migrations) } + pub fn contains(&self, migration: &Migration) -> bool { + self.0.contains(migration) + } + + pub fn len(&self) -> usize { + self.0.len() + } + pub fn origins(&self) -> Vec { self.0.iter().map(|migration| migration.origin.clone()).collect() } diff --git a/runtimes/testnet/src/lib.rs b/runtimes/testnet/src/lib.rs index 14b1f490a..263a3b0ba 100644 --- a/runtimes/testnet/src/lib.rs +++ b/runtimes/testnet/src/lib.rs @@ -671,11 +671,11 @@ impl pallet_funding::Config for Runtime { type InvestorOrigin = EnsureInvestor; type ManualAcceptanceDuration = ManualAcceptanceDuration; type MaxBidsPerProject = ConstU32<1024>; - type MaxBidsPerUser = ConstU32<32>; + type MaxBidsPerUser = ConstU32<16>; type MaxCapacityThresholds = MaxCapacityThresholds; - type MaxContributionsPerUser = ConstU32<32>; + type MaxContributionsPerUser = ConstU32<16>; type MaxEvaluationsPerProject = ConstU32<1024>; - type MaxEvaluationsPerUser = ConstU32<32>; + type MaxEvaluationsPerUser = ConstU32<16>; type MaxMessageSizeThresholds = MaxMessageSizeThresholds; type MaxProjectsToUpdateInsertionAttempts = ConstU32<100>; type MaxProjectsToUpdatePerBlock = ConstU32<1>; From f22d0e9e2c2af38defe55bfb6a398298f055b6ac Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Mon, 25 Mar 2024 20:31:54 +0100 Subject: [PATCH 14/21] address feedback --- pallets/funding/src/instantiator.rs | 22 +++++++++++----------- pallets/funding/src/settlement.rs | 13 +++++-------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index 8ff05b64d..b6d40a9fb 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1342,7 +1342,7 @@ impl< self.execute(||{ match details.status { ProjectStatus::FundingSuccessful => Self::settle_successful_project(project_id), - ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => Self::settle_failed_project(project_id, details.status), + ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => Self::settle_failed_project(project_id), _ => panic!("Project should be in FundingSuccessful, FundingFailed or EvaluationFailed status"), } }) @@ -1362,19 +1362,19 @@ impl< }) } - fn settle_failed_project(project_id: ProjectId, status: ProjectStatus) -> Result<(), DispatchError> { + fn settle_failed_project(project_id: ProjectId) -> Result<(), DispatchError> { Evaluations::::iter_prefix((project_id,)).try_for_each(|(_, evaluation)| { Pallet::::do_settle_failed_evaluation(evaluation, project_id) })?; - if status == ProjectStatus::FundingFailed { - Bids::::iter_prefix((project_id,)).try_for_each(|(_, bid)| { - Pallet::::do_settle_failed_bid(bid, project_id) - })?; - - Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { - Pallet::::do_settle_failed_contribution(contribution, project_id) - })?; - } + + Bids::::iter_prefix((project_id,)).try_for_each(|(_, bid)| { + Pallet::::do_settle_failed_bid(bid, project_id) + })?; + + Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { + Pallet::::do_settle_failed_contribution(contribution, project_id) + })?; + Ok(()) } diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index e298fcfd3..6c15be02c 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -68,12 +68,11 @@ impl Pallet { Error::::NotAllowed ); - let bond; - if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { - bond = Self::slash_evaluator(project_id, &evaluation)?; + let bond = if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { + Self::slash_evaluator(project_id, &evaluation)? } else { - bond = evaluation.current_plmc_bond; - } + evaluation.current_plmc_bond + }; // Release the held PLMC bond @@ -137,7 +136,6 @@ impl Pallet { Self::release_funding_asset(project_id, &project_details.issuer_account, bid.funding_asset_amount_locked, bid.funding_asset)?; Self::create_migration(project_id, &bidder, bid.id, ParticipationType::Bid, bid.final_ct_amount, vest_info.duration)?; - // TODO: Create MigrationInfo Bids::::remove((project_id, bidder.clone(), bid.id)); @@ -333,8 +331,7 @@ impl Pallet { ); let early_evaluators_rewards = early_reward_weight * info.early_evaluator_reward_pot; let normal_evaluators_rewards = normal_reward_weight * info.normal_evaluator_reward_pot; - let total_reward_amount = early_evaluators_rewards.saturating_add(normal_evaluators_rewards); - total_reward_amount + early_evaluators_rewards.saturating_add(normal_evaluators_rewards) } pub fn create_migration( From 93f06437f21ed8ec5fe619cf42309f6e3c9472c1 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Mon, 25 Mar 2024 20:32:30 +0100 Subject: [PATCH 15/21] fmt --- integration-tests/src/tests/ct_migration.rs | 65 +- nodes/parachain/src/chain_spec/politest.rs | 2 +- pallets/funding/src/benchmarking.rs | 42 +- pallets/funding/src/functions.rs | 61 +- pallets/funding/src/instantiator.rs | 150 +++-- pallets/funding/src/lib.rs | 38 +- pallets/funding/src/settlement.rs | 635 ++++++++++---------- pallets/funding/src/tests.rs | 251 ++++++-- pallets/funding/src/types.rs | 12 +- polimec-common/common/src/lib.rs | 2 +- 10 files changed, 719 insertions(+), 539 deletions(-) diff --git a/integration-tests/src/tests/ct_migration.rs b/integration-tests/src/tests/ct_migration.rs index 8b5bdc854..0082101a2 100644 --- a/integration-tests/src/tests/ct_migration.rs +++ b/integration-tests/src/tests/ct_migration.rs @@ -15,12 +15,8 @@ // along with this program. If not, see . use crate::*; -use pallet_funding::{ - assert_close_enough, ProjectId, -}; -use polimec_common::{ - migration_types::{Migrations, MigrationStatus}, -}; +use pallet_funding::{assert_close_enough, ProjectId}; +use polimec_common::migration_types::{MigrationStatus, Migrations}; use politest_runtime::PolimecFunding; use sp_runtime::Perquintill; use std::collections::HashMap; @@ -49,12 +45,16 @@ fn assert_migration_is_ready(project_id: u32) { }); } -fn get_migrations_for_participants(project_id: ProjectId, participants: Vec) -> HashMap { +fn get_migrations_for_participants( + project_id: ProjectId, + participants: Vec, +) -> HashMap { let mut user_migrations = HashMap::new(); PolitestNet::execute_with(|| { for participant in participants { - let (status, migrations) = pallet_funding::UserMigrations::::get(project_id, participant.clone()).unwrap(); - user_migrations.insert(participant, (status, Migrations::from(migrations.into()))); + let (status, migrations) = + pallet_funding::UserMigrations::::get(project_id, participant.clone()).unwrap(); + user_migrations.insert(participant, (status, Migrations::from(migrations.into()))); } }); user_migrations @@ -74,24 +74,24 @@ fn send_migrations(project_id: ProjectId, accounts: Vec) { fn migrations_are_executed(project_id: ProjectId, accounts: Vec) { let user_migrations = get_migrations_for_participants(project_id, accounts.clone()); - for account in accounts.into_iter(){ + for account in accounts.into_iter() { let user_info = PenNet::account_data_of(account.clone()); PenNet::execute_with(|| { - let (_, migrations) = user_migrations.get(&account).unwrap(); - let matched_events = PenNet::events().iter().filter(|event| { - match event { - PenpalEvent::PolimecReceiver(polimec_receiver::Event::MigrationExecuted{migration}) => { - migrations.contains(&migration) - }, + let matched_events = PenNet::events() + .iter() + .filter(|event| match event { + PenpalEvent::PolimecReceiver(polimec_receiver::Event::MigrationExecuted { migration }) => + migrations.contains(&migration), _ => false, - } - }).count(); + }) + .count(); assert_eq!(matched_events, migrations.len()); assert_close_enough!(user_info.free, migrations.total_ct_amount(), Perquintill::from_float(0.99)); - let vest_scheduled_cts = migrations.clone() + let vest_scheduled_cts = migrations + .clone() .inner() .iter() .filter_map(|migration| { @@ -113,28 +113,27 @@ fn migrations_are_confirmed(project_id: u32, accounts: Vec) { for user in accounts.iter() { let (current_status, _) = user_migrations.get(user).unwrap(); assert_eq!(current_status, &MigrationStatus::Confirmed); - - let matched_events: usize = PolitestNet::events().iter().filter(|event| { - match event { - PolitestEvent::PolimecFunding(pallet_funding::Event::MigrationStatusUpdated{project_id, account, status}) => { - project_id == project_id && account == user && - matches!(status, &MigrationStatus::Confirmed) - }, + + let matched_events: usize = PolitestNet::events() + .iter() + .filter(|event| match event { + PolitestEvent::PolimecFunding(pallet_funding::Event::MigrationStatusUpdated { + project_id, + account, + status, + }) => project_id == project_id && account == user && matches!(status, &MigrationStatus::Confirmed), _ => false, - } - }).count(); + }) + .count(); assert_eq!(matched_events, 1); - - } }); } fn vest_migrations(project_id: u32, accounts: Vec) { let user_migrations = get_migrations_for_participants(project_id, accounts.clone()); - let biggest_time = user_migrations.iter().map(|(_, (_, migrations))| { - migrations.biggest_vesting_time() - }).max().unwrap(); + let biggest_time = + user_migrations.iter().map(|(_, (_, migrations))| migrations.biggest_vesting_time()).max().unwrap(); PenNet::execute_with(|| { PenpalSystem::set_block_number(biggest_time as u32 + 1u32); diff --git a/nodes/parachain/src/chain_spec/politest.rs b/nodes/parachain/src/chain_spec/politest.rs index d958cb777..9d9610c56 100644 --- a/nodes/parachain/src/chain_spec/politest.rs +++ b/nodes/parachain/src/chain_spec/politest.rs @@ -325,11 +325,11 @@ fn testing_genesis( sudo_account: AccountId, id: ParaId, ) -> RuntimeGenesisConfig { + use pallet_funding::instantiator::TestProjectParams; use politest_runtime::{ BalancesConfig, CouncilConfig, ForeignAssetsConfig, ParachainInfoConfig, ParachainStakingConfig, PolkadotXcmConfig, SessionConfig, SudoConfig, TechnicalCommitteeConfig, }; - use pallet_funding::instantiator::TestProjectParams; use testing_helpers::*; // only used to generate some values, and not for chain interactions diff --git a/pallets/funding/src/benchmarking.rs b/pallets/funding/src/benchmarking.rs index 74e0a1c03..1a8c48a49 100644 --- a/pallets/funding/src/benchmarking.rs +++ b/pallets/funding/src/benchmarking.rs @@ -635,16 +635,12 @@ mod benchmarks { .unwrap(); let correct = match stored_evaluation { - EvaluationInfo { - project_id, - evaluator, - original_plmc_bond, - current_plmc_bond, - .. - } if project_id == project_id && - evaluator == evaluation.account.clone() && - original_plmc_bond == extrinsic_plmc_bonded && - current_plmc_bond == extrinsic_plmc_bonded => true, + EvaluationInfo { project_id, evaluator, original_plmc_bond, current_plmc_bond, .. } + if project_id == project_id && + evaluator == evaluation.account.clone() && + original_plmc_bond == extrinsic_plmc_bonded && + current_plmc_bond == extrinsic_plmc_bonded => + true, _ => false, }; assert!(correct, "Evaluation is not stored correctly"); @@ -1452,7 +1448,6 @@ mod benchmarks { // Evaluation should be removed assert!(Evaluations::::get((project_id, evaluator.clone(), evaluation_to_settle.id)).is_none()); - // Balances let project_details = ProjectsDetails::::get(project_id).unwrap(); let reward_info = match project_details.evaluation_round_info.evaluators_outcome { @@ -1460,7 +1455,7 @@ mod benchmarks { _ => panic!("EvaluatorsOutcome should be Rewarded"), }; let reward = Pallet::::calculate_evaluator_reward(&evaluation_to_settle, &reward_info); - + let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![evaluator.clone()])[0]; assert_eq!(ct_amount, reward); @@ -1532,7 +1527,9 @@ mod benchmarks { assert!(Evaluations::::get((project_id, evaluator.clone(), evaluation_to_settle.id)).is_none()); let slashed_amount = T::EvaluatorSlash::get() * evaluation_to_settle.original_plmc_bond; - let reserved_plmc = inst.get_reserved_plmc_balances_for(vec![evaluator.clone()], HoldReason::Evaluation(project_id).into())[0].plmc_amount; + let reserved_plmc = inst + .get_reserved_plmc_balances_for(vec![evaluator.clone()], HoldReason::Evaluation(project_id).into())[0] + .plmc_amount; assert_eq!(reserved_plmc, 0.into()); let treasury_account = T::ProtocolGrowthTreasury::get(); @@ -1547,7 +1544,7 @@ mod benchmarks { account: evaluator.clone(), id: evaluation_to_settle.id, ct_amount: 0.into(), - slashed_amount: slashed_amount, + slashed_amount, } .into(), ); @@ -1589,20 +1586,13 @@ mod benchmarks { // Storage assert!(Bids::::get((project_id, bidder.clone(), bid_to_settle.id)).is_none()); - // Balances let ct_amount = inst.get_ct_asset_balances_for(project_id, vec![bidder.clone()])[0]; assert_eq!(bid_to_settle.final_ct_amount, ct_amount); // Events frame_system::Pallet::::assert_last_event( - Event::BidSettled { - project_id, - account: bidder.clone(), - id: bid_to_settle.id, - ct_amount, - } - .into(), + Event::BidSettled { project_id, account: bidder.clone(), id: bid_to_settle.id, ct_amount }.into(), ); } @@ -1661,13 +1651,7 @@ mod benchmarks { // Events frame_system::Pallet::::assert_last_event( - Event::BidSettled { - project_id, - account: bidder.clone(), - id: bid_to_settle.id, - ct_amount: 0.into(), - } - .into(), + Event::BidSettled { project_id, account: bidder.clone(), id: bid_to_settle.id, ct_amount: 0.into() }.into(), ); } diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index d5bd8e0a7..9b77993aa 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -30,15 +30,12 @@ use frame_support::{ }, }; use frame_system::pallet_prelude::BlockNumberFor; -use polimec_common::{ - credentials::{Did, InvestorType}, -}; +use polimec_common::credentials::{Did, InvestorType}; use sp_arithmetic::{ traits::{CheckedDiv, CheckedSub, Zero}, Percent, Perquintill, }; -use sp_runtime::traits::{Convert}; - +use sp_runtime::traits::Convert; use super::*; use crate::traits::{BondingRequirementCalculation, ProvideAssetPrice, VestingDurationCalculation}; @@ -307,7 +304,6 @@ impl Pallet { let issuer_did = project_details.issuer_did.clone(); DidWithActiveProjects::::set(issuer_did, None); - // * Emit events * Self::deposit_event(Event::EvaluationFailed { project_id }); return Ok(PostDispatchInfo { @@ -1742,16 +1738,14 @@ impl Pallet { Ok(()) } - pub fn do_migrate_one_participant( - project_id: ProjectId, - participant: AccountIdOf, - ) -> DispatchResult { + pub fn do_migrate_one_participant(project_id: ProjectId, participant: AccountIdOf) -> DispatchResult { // * Get variables * let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; let migration_readiness_check = project_details.migration_readiness_check.ok_or(Error::::NotAllowed)?; let project_para_id = project_details.parachain_id.ok_or(Error::::ImpossibleState)?; let now = >::block_number(); - let (_, migrations) = UserMigrations::::get(project_id, participant.clone()).ok_or(Error::::NoMigrationsFound)?; + let (_, migrations) = + UserMigrations::::get(project_id, participant.clone()).ok_or(Error::::NoMigrationsFound)?; // * Validity Checks * ensure!(migration_readiness_check.is_ready(), Error::::NotAllowed); @@ -1761,7 +1755,7 @@ impl Pallet { Call::confirm_migrations { query_id: Default::default(), response: Default::default() }.into(); let query_id = pallet_xcm::Pallet::::new_notify_query(project_multilocation, call.into(), now + 20u32.into(), Here); - + Self::change_migration_status(project_id, participant.clone(), MigrationStatus::Sent(query_id))?; // * Process Data * @@ -1773,7 +1767,7 @@ impl Pallet { Self::deposit_event(Event::::MigrationStatusUpdated { project_id, account: participant, - status: MigrationStatus::Sent(query_id) + status: MigrationStatus::Sent(query_id), }); Ok(()) @@ -1784,8 +1778,10 @@ impl Pallet { let (project_id, participant) = ActiveMigrationQueue::::take(query_id)?; let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!(matches!(location, MultiLocation { parents: 1, interior: X1(Parachain(para_id))} if Some(ParaId::from(para_id)) == project_details.parachain_id), Error::::NotAllowed); - + ensure!( + matches!(location, MultiLocation { parents: 1, interior: X1(Parachain(para_id))} if Some(ParaId::from(para_id)) == project_details.parachain_id), + Error::::NotAllowed + ); let status = match response { Response::DispatchResult(MaybeErrorCode::Success) => { @@ -1799,11 +1795,7 @@ impl Pallet { }, _ => return Err(Error::::NotAllowed.into()), }; - Self::deposit_event(Event::::MigrationStatusUpdated{ - project_id, - account: participant, - status: status, - }); + Self::deposit_event(Event::::MigrationStatusUpdated { project_id, account: participant, status }); Ok(()) } } @@ -1891,8 +1883,6 @@ impl Pallet { let project_account = Self::fund_account_id(project_id); let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PLMCPriceNotAvailable)?; - - // sort bids by price, and equal prices sorted by id bids.sort_by(|a, b| b.cmp(a)); // accept only bids that were made before `end_block` i.e end of candle auction @@ -1969,13 +1959,13 @@ impl Pallet { let weighted_price = bid.original_ct_usd_price.saturating_mul(bid_weight); weighted_price }; - let weighted_token_price = if is_first_bucket { + let weighted_token_price = if is_first_bucket { 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)) + .iter() + .map(calc_weighted_price_fn) + .fold(Zero::zero(), |a: T::Price, b: T::Price| a.saturating_add(b)) }; let mut final_total_funding_reached_by_bids = BalanceOf::::zero(); @@ -2049,12 +2039,11 @@ impl Pallet { } /// Refund a bid because of `reason`. - fn refund_bid<>( + fn refund_bid( bid: &BidInfoOf, project_id: ProjectId, project_account: &AccountIdOf, ) -> Result<(), DispatchError> { - T::FundingCurrency::transfer( bid.funding_asset.to_assethub_id(), project_account, @@ -2368,23 +2357,22 @@ impl Pallet { encoded_call.extend_from_slice(migrations_item.encode().as_slice()); Xcm(vec![ UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: MAX_WEIGHT, - call: encoded_call.into(), - }, + Transact { origin_kind: OriginKind::Native, require_weight_at_most: MAX_WEIGHT, call: encoded_call.into() }, ReportTransactStatus(QueryResponseInfo { destination: ParentThen(X1(Parachain(POLIMEC_PARA_ID))).into(), query_id, max_weight: MAX_RESPONSE_WEIGHT, - }) + }), ]) } fn change_migration_status(project_id: ProjectId, user: T::AccountId, status: MigrationStatus) -> DispatchResult { - let (current_status, migrations) = UserMigrations::::get(project_id, user.clone()).ok_or(Error::::NoMigrationsFound)?; + let (current_status, migrations) = + UserMigrations::::get(project_id, user.clone()).ok_or(Error::::NoMigrationsFound)?; let status = match status { - MigrationStatus::Sent(_) if matches!(current_status, MigrationStatus::NotStarted | MigrationStatus::Failed) => status, + MigrationStatus::Sent(_) + if matches!(current_status, MigrationStatus::NotStarted | MigrationStatus::Failed) => + status, MigrationStatus::Confirmed if matches!(current_status, MigrationStatus::Sent(_)) => status, MigrationStatus::Failed if matches!(current_status, MigrationStatus::Sent(_)) => status, _ => return Err(Error::::NotAllowed.into()), @@ -2392,5 +2380,4 @@ impl Pallet { UserMigrations::::insert(project_id, user, (status, migrations)); Ok(()) } - } diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index b6d40a9fb..09390212f 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1339,42 +1339,34 @@ impl< pub fn settle_project(&mut self, project_id: ProjectId) -> Result<(), DispatchError> { let details = self.get_project_details(project_id); - self.execute(||{ - match details.status { - ProjectStatus::FundingSuccessful => Self::settle_successful_project(project_id), - ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => Self::settle_failed_project(project_id), - _ => panic!("Project should be in FundingSuccessful, FundingFailed or EvaluationFailed status"), - } + self.execute(|| match details.status { + ProjectStatus::FundingSuccessful => Self::settle_successful_project(project_id), + ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed => Self::settle_failed_project(project_id), + _ => panic!("Project should be in FundingSuccessful, FundingFailed or EvaluationFailed status"), }) } fn settle_successful_project(project_id: ProjectId) -> Result<(), DispatchError> { - Evaluations::::iter_prefix((project_id,)).try_for_each(|(_, evaluation)| { - Pallet::::do_settle_successful_evaluation(evaluation, project_id) - })?; + Evaluations::::iter_prefix((project_id,)) + .try_for_each(|(_, evaluation)| Pallet::::do_settle_successful_evaluation(evaluation, project_id))?; - Bids::::iter_prefix((project_id,)).try_for_each(|(_, bid)| { - Pallet::::do_settle_successful_bid(bid, project_id) - })?; + Bids::::iter_prefix((project_id,)) + .try_for_each(|(_, bid)| Pallet::::do_settle_successful_bid(bid, project_id))?; - Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { - Pallet::::do_settle_successful_contribution(contribution, project_id) - }) + Contributions::::iter_prefix((project_id,)) + .try_for_each(|(_, contribution)| Pallet::::do_settle_successful_contribution(contribution, project_id)) } fn settle_failed_project(project_id: ProjectId) -> Result<(), DispatchError> { - Evaluations::::iter_prefix((project_id,)).try_for_each(|(_, evaluation)| { - Pallet::::do_settle_failed_evaluation(evaluation, project_id) - })?; - - Bids::::iter_prefix((project_id,)).try_for_each(|(_, bid)| { - Pallet::::do_settle_failed_bid(bid, project_id) - })?; - - Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { - Pallet::::do_settle_failed_contribution(contribution, project_id) - })?; - + Evaluations::::iter_prefix((project_id,)) + .try_for_each(|(_, evaluation)| Pallet::::do_settle_failed_evaluation(evaluation, project_id))?; + + Bids::::iter_prefix((project_id,)) + .try_for_each(|(_, bid)| Pallet::::do_settle_failed_bid(bid, project_id))?; + + Contributions::::iter_prefix((project_id,)) + .try_for_each(|(_, contribution)| Pallet::::do_settle_failed_contribution(contribution, project_id))?; + Ok(()) } @@ -1392,51 +1384,92 @@ impl< // Used to check if all evaluations are settled correctly. We cannot check amount of // contributions minted for the user, as they could have received more tokens from other participations. - pub fn assert_evaluations_settled(&mut self, project_id: ProjectId, evaluations: Vec>, percentage: u64) { + pub fn assert_evaluations_settled( + &mut self, + project_id: ProjectId, + evaluations: Vec>, + percentage: u64, + ) { let details = self.get_project_details(project_id); - assert!(matches!(details.status, ProjectStatus::FundingSuccessful | ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed)); - - self.execute(||{ - for evaluation in evaluations { - let reward_info = ProjectsDetails::::get(project_id).unwrap().evaluation_round_info.evaluators_outcome; + assert!(matches!( + details.status, + ProjectStatus::FundingSuccessful | ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed + )); + + self.execute(|| { + for evaluation in evaluations { + let reward_info = + ProjectsDetails::::get(project_id).unwrap().evaluation_round_info.evaluators_outcome; let account = evaluation.evaluator.clone(); assert_eq!(Evaluations::::iter_prefix_values((&project_id, &account)).count(), 0); - let reserved = ::NativeCurrency::balance_on_hold(&(HoldReason::Evaluation(project_id).into()), &account); + let reserved = ::NativeCurrency::balance_on_hold( + &(HoldReason::Evaluation(project_id).into()), + &account, + ); assert_eq!(reserved, 0u64.into()); match percentage { 0..=75 => { assert!(::NativeCurrency::balance(&account) < evaluation.current_plmc_bond); assert!(matches!(reward_info, EvaluatorsOutcome::Slashed)); - Self::assert_migration(project_id, account, 0u64.into(), evaluation.id, ParticipationType::Evaluation, false); + Self::assert_migration( + project_id, + account, + 0u64.into(), + evaluation.id, + ParticipationType::Evaluation, + false, + ); }, 76..=89 => { assert!(::NativeCurrency::balance(&account) >= evaluation.current_plmc_bond); assert!(matches!(reward_info, EvaluatorsOutcome::Unchanged)); - Self::assert_migration(project_id, account, 0u64.into(), evaluation.id, ParticipationType::Evaluation, false); + Self::assert_migration( + project_id, + account, + 0u64.into(), + evaluation.id, + ParticipationType::Evaluation, + false, + ); }, 90..=100 => { assert!(::NativeCurrency::balance(&account) >= evaluation.current_plmc_bond); let reward = match reward_info { - EvaluatorsOutcome::Rewarded(info) => Pallet::::calculate_evaluator_reward(&evaluation, &info), - _ => panic!("Evaluators should be rewarded") + EvaluatorsOutcome::Rewarded(info) => + Pallet::::calculate_evaluator_reward(&evaluation, &info), + _ => panic!("Evaluators should be rewarded"), }; - Self::assert_migration(project_id, account, reward, evaluation.id, ParticipationType::Evaluation, true); + Self::assert_migration( + project_id, + account, + reward, + evaluation.id, + ParticipationType::Evaluation, + true, + ); }, - _ => panic!("Percentage should be between 0 and 100") + _ => panic!("Percentage should be between 0 and 100"), } } }); } - // Testing if a list of bids are settled correctly. + // Testing if a list of bids are settled correctly. pub fn assert_bids_settled(&mut self, project_id: ProjectId, bids: Vec>, is_successful: bool) { - self.execute(||{ + self.execute(|| { for bid in bids { let account = bid.bidder.clone(); assert_eq!(Bids::::iter_prefix_values((&project_id, &account)).count(), 0); if is_successful { - Self::assert_migration(project_id, account, bid.final_ct_amount, bid.id, ParticipationType::Bid, true); + Self::assert_migration( + project_id, + account, + bid.final_ct_amount, + bid.id, + ParticipationType::Bid, + true, + ); } else { Self::assert_migration(project_id, account, 0u64.into(), bid.id, ParticipationType::Bid, false); } @@ -1445,15 +1478,34 @@ impl< } // Testing if a list of contributions are settled correctly. - pub fn assert_contributions_settled(&mut self, project_id: ProjectId, contributions: Vec>, is_successful: bool) { - self.execute(||{ + pub fn assert_contributions_settled( + &mut self, + project_id: ProjectId, + contributions: Vec>, + is_successful: bool, + ) { + self.execute(|| { for contribution in contributions { let account = contribution.contributor.clone(); assert_eq!(Bids::::iter_prefix_values((&project_id, &account)).count(), 0); if is_successful { - Self::assert_migration(project_id, account, contribution.ct_amount, contribution.id, ParticipationType::Contribution, true); + Self::assert_migration( + project_id, + account, + contribution.ct_amount, + contribution.id, + ParticipationType::Contribution, + true, + ); } else { - Self::assert_migration(project_id, account, 0u64.into(), contribution.id, ParticipationType::Contribution, false); + Self::assert_migration( + project_id, + account, + 0u64.into(), + contribution.id, + ParticipationType::Contribution, + false, + ); } } }); @@ -1467,7 +1519,7 @@ impl< participation_type: ParticipationType, should_exist: bool, ) { - let correct = match (should_exist, UserMigrations::::get(project_id, account.clone())){ + let correct = match (should_exist, UserMigrations::::get(project_id, account.clone())) { // User has migrations, so we need to check if any matches our criteria (_, Some((_, migrations))) => { let maybe_migration = migrations.into_iter().find(|migration| { @@ -1478,7 +1530,7 @@ impl< // Migration exists so we check if the amount is correct and if it should exist Some(migration) => migration.info.contribution_token_amount == amount.into() && should_exist, // Migration doesn't exist so we check if it should not exist - None => !should_exist + None => !should_exist, } }, // User does not have any migrations, so the migration should not exist diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 1a4b15e14..877f89402 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -177,8 +177,7 @@ pub type EvaluationInfoOf = EvaluationInfo, Ba pub type BidInfoOf = BidInfo, PriceOf, AccountIdOf, BlockNumberFor, MultiplierOf>; -pub type ContributionInfoOf = - ContributionInfo, BalanceOf, MultiplierOf>; +pub type ContributionInfoOf = ContributionInfo, BalanceOf, MultiplierOf>; pub type BucketOf = Bucket, PriceOf>; pub type WeightInfoOf = ::WeightInfo; @@ -197,10 +196,10 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use local_macros::*; -use sp_arithmetic::Percent; + use sp_arithmetic::Percent; use sp_runtime::{ - traits::{Convert, ConvertBack, Get}, - DispatchErrorWithPostInfo + traits::{Convert, ConvertBack, Get}, + DispatchErrorWithPostInfo, }; #[cfg(any(feature = "runtime-benchmarks", feature = "std"))] @@ -551,10 +550,10 @@ use sp_arithmetic::Percent; Blake2_128Concat, ProjectId, Blake2_128Concat, - T::AccountId, + T::AccountId, (MigrationStatus, BoundedVec>), >; - + pub struct MaxParticipationsPerUser(PhantomData); impl Get for MaxParticipationsPerUser { fn get() -> u32 { @@ -563,7 +562,13 @@ use sp_arithmetic::Percent; } #[pallet::storage] - pub type ActiveMigrationQueue = StorageMap<_, Blake2_128Concat, QueryId, (ProjectId, T::AccountId), ResultQuery::NoActiveMigrationsFound>>; + pub type ActiveMigrationQueue = StorageMap< + _, + Blake2_128Concat, + QueryId, + (ProjectId, T::AccountId), + ResultQuery::NoActiveMigrationsFound>, + >; /// A map to keep track of what issuer's did has an active project. It prevents one issuer having multiple active projects #[pallet::storage] @@ -999,13 +1004,14 @@ use sp_arithmetic::Percent; evaluation_id: u32, ) -> DispatchResult { let _caller = ensure_signed(origin)?; - let bid = Evaluations::::get((project_id, evaluator, evaluation_id)).ok_or(Error::::ParticipationNotFound)?; + let bid = Evaluations::::get((project_id, evaluator, evaluation_id)) + .ok_or(Error::::ParticipationNotFound)?; Self::do_settle_successful_evaluation(bid, project_id) } #[pallet::call_index(10)] #[pallet::weight(Weight::from_parts(0, 0))] - pub fn settle_successful_bid ( + pub fn settle_successful_bid( origin: OriginFor, project_id: ProjectId, bidder: AccountIdOf, @@ -1025,7 +1031,8 @@ use sp_arithmetic::Percent; contribution_id: u32, ) -> DispatchResult { let _caller = ensure_signed(origin)?; - let bid = Contributions::::get((project_id, contributor, contribution_id)).ok_or(Error::::ParticipationNotFound)?; + let bid = Contributions::::get((project_id, contributor, contribution_id)) + .ok_or(Error::::ParticipationNotFound)?; Self::do_settle_successful_contribution(bid, project_id) } @@ -1038,13 +1045,14 @@ use sp_arithmetic::Percent; evaluation_id: u32, ) -> DispatchResult { let _caller = ensure_signed(origin)?; - let bid = Evaluations::::get((project_id, evaluator, evaluation_id)).ok_or(Error::::ParticipationNotFound)?; + let bid = Evaluations::::get((project_id, evaluator, evaluation_id)) + .ok_or(Error::::ParticipationNotFound)?; Self::do_settle_failed_evaluation(bid, project_id) } #[pallet::call_index(13)] #[pallet::weight(Weight::from_parts(0, 0))] - pub fn settle_failed_bid ( + pub fn settle_failed_bid( origin: OriginFor, project_id: ProjectId, bidder: AccountIdOf, @@ -1064,11 +1072,11 @@ use sp_arithmetic::Percent; contribution_id: u32, ) -> DispatchResult { let _caller = ensure_signed(origin)?; - let bid = Contributions::::get((project_id, contributor, contribution_id)).ok_or(Error::::ParticipationNotFound)?; + let bid = Contributions::::get((project_id, contributor, contribution_id)) + .ok_or(Error::::ParticipationNotFound)?; Self::do_settle_failed_contribution(bid, project_id) } - #[pallet::call_index(22)] #[pallet::weight(Weight::from_parts(1000, 0))] pub fn set_para_id_for_project( diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index 6c15be02c..3c4243694 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -5,293 +5,321 @@ use frame_support::{ ensure, pallet_prelude::*, traits::{ - fungible::{ MutateHold as FungibleMutateHold}, - fungibles::{ - Inspect, Mutate as FungiblesMutate, - }, + fungible::MutateHold as FungibleMutateHold, + fungibles::{Inspect, Mutate as FungiblesMutate}, tokens::{Fortitude, Precision, Preservation, Restriction}, Get, }, }; -use sp_runtime::{Perquintill, traits::{Zero, Convert}}; use polimec_common::{ migration_types::{MigrationInfo, MigrationOrigin, MigrationStatus, ParticipationType}, ReleaseSchedule, }; +use sp_runtime::{ + traits::{Convert, Zero}, + Perquintill, +}; impl Pallet { - pub fn do_settle_successful_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::::NotAllowed); - - // Based on the results of the funding round, the evaluator is either: - // 1. Slashed - // 2. Rewarded with CT tokens - // 3. Not slashed or Rewarded. - let (bond, reward): (BalanceOf, BalanceOf) = match project_details.evaluation_round_info.evaluators_outcome { - EvaluatorsOutcome::Slashed => (Self::slash_evaluator(project_id, &evaluation)?, Zero::zero()), - EvaluatorsOutcome::Rewarded(info) => Self::reward_evaluator(project_id, &evaluation, &info)?, - EvaluatorsOutcome::Unchanged => (evaluation.current_plmc_bond, Zero::zero()), - }; - - // Release the held PLMC bond - T::NativeCurrency::release( - &HoldReason::Evaluation(project_id).into(), - &evaluation.evaluator, - bond, - Precision::Exact, - )?; - - // Create Migration - if reward > Zero::zero() { - let multiplier = MultiplierOf::::try_from(1u8).map_err(|_| Error::::BadMath)?; - let duration = multiplier.calculate_vesting_duration::(); - Self::create_migration(project_id, &evaluation.evaluator, evaluation.id, ParticipationType::Evaluation, reward, duration)?; - } - Evaluations::::remove((project_id, evaluation.evaluator.clone(), evaluation.id)); - - Self::deposit_event(Event::EvaluationSettled { - project_id, - account: evaluation.evaluator, - id: evaluation.id, - ct_amount: reward, - slashed_amount: evaluation.current_plmc_bond.saturating_sub(bond), - }); - - Ok(()) - } - - pub fn do_settle_failed_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!( + pub fn do_settle_successful_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::::NotAllowed); + + // Based on the results of the funding round, the evaluator is either: + // 1. Slashed + // 2. Rewarded with CT tokens + // 3. Not slashed or Rewarded. + let (bond, reward): (BalanceOf, BalanceOf) = + match project_details.evaluation_round_info.evaluators_outcome { + EvaluatorsOutcome::Slashed => (Self::slash_evaluator(project_id, &evaluation)?, Zero::zero()), + EvaluatorsOutcome::Rewarded(info) => Self::reward_evaluator(project_id, &evaluation, &info)?, + EvaluatorsOutcome::Unchanged => (evaluation.current_plmc_bond, Zero::zero()), + }; + + // Release the held PLMC bond + T::NativeCurrency::release( + &HoldReason::Evaluation(project_id).into(), + &evaluation.evaluator, + bond, + Precision::Exact, + )?; + + // Create Migration + if reward > Zero::zero() { + let multiplier = MultiplierOf::::try_from(1u8).map_err(|_| Error::::BadMath)?; + let duration = multiplier.calculate_vesting_duration::(); + Self::create_migration( + project_id, + &evaluation.evaluator, + evaluation.id, + ParticipationType::Evaluation, + reward, + duration, + )?; + } + Evaluations::::remove((project_id, evaluation.evaluator.clone(), evaluation.id)); + + Self::deposit_event(Event::EvaluationSettled { + project_id, + account: evaluation.evaluator, + id: evaluation.id, + ct_amount: reward, + slashed_amount: evaluation.current_plmc_bond.saturating_sub(bond), + }); + + Ok(()) + } + + pub fn do_settle_failed_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!( matches!(project_details.status, ProjectStatus::FundingFailed | ProjectStatus::EvaluationFailed), Error::::NotAllowed ); - let bond = if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { - Self::slash_evaluator(project_id, &evaluation)? - } else { - evaluation.current_plmc_bond - }; - + let bond = if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { + Self::slash_evaluator(project_id, &evaluation)? + } else { + evaluation.current_plmc_bond + }; - // Release the held PLMC bond - T::NativeCurrency::release( + // Release the held PLMC bond + T::NativeCurrency::release( &HoldReason::Evaluation(project_id).into(), &evaluation.evaluator, bond, Precision::Exact, )?; - Evaluations::::remove((project_id, evaluation.evaluator.clone(), evaluation.id)); + Evaluations::::remove((project_id, evaluation.evaluator.clone(), evaluation.id)); - Self::deposit_event(Event::EvaluationSettled { - project_id, - account: evaluation.evaluator, - id: evaluation.id, - ct_amount: Zero::zero(), - slashed_amount: evaluation.current_plmc_bond.saturating_sub(bond), - }); + Self::deposit_event(Event::EvaluationSettled { + project_id, + account: evaluation.evaluator, + id: evaluation.id, + ct_amount: Zero::zero(), + slashed_amount: evaluation.current_plmc_bond.saturating_sub(bond), + }); - Ok(()) - } + Ok(()) + } - pub fn do_settle_successful_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { - - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + pub fn do_settle_successful_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; // Ensure that: - // 1. The project is in the FundingSuccessful state - // 2. The bid is in the Accepted or PartiallyAccepted state - // 3. The contribution token exists - ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); - ensure!(matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), Error::::NotAllowed); - ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); + // 1. The project is in the FundingSuccessful state + // 2. The bid is in the Accepted or PartiallyAccepted state + // 3. The contribution token exists + ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); + ensure!(matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), Error::::NotAllowed); + ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); - let bidder = bid.bidder; + let bidder = bid.bidder; - // Calculate the vesting info and add the release schedule - let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; + // Calculate the vesting info and add the release schedule + let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; let vest_info = Self::calculate_vesting_info(&bidder, bid.multiplier, bid.plmc_bond).map_err(|_| Error::::BadMath)?; - - // If the multiplier is greater than 1, add the release schedule else release the held PLMC bond - if bid.multiplier.into() > 1u8 { - T::Vesting::add_release_schedule( - &bidder, - vest_info.total_amount, - vest_info.amount_per_block, - funding_end_block, - HoldReason::Participation(project_id).into(), - )?; - } else { - // Release the held PLMC bond - Self::release_bond(project_id, &bidder, bid.plmc_bond)?; - } - - - // Mint the contribution tokens - Self::mint_ct_tokens(project_id, &bidder, bid.final_ct_amount)?; - - // Payout the bid funding asset amount to the project account - Self::release_funding_asset(project_id, &project_details.issuer_account, bid.funding_asset_amount_locked, bid.funding_asset)?; - - Self::create_migration(project_id, &bidder, bid.id, ParticipationType::Bid, bid.final_ct_amount, vest_info.duration)?; - - Bids::::remove((project_id, bidder.clone(), bid.id)); - - Self::deposit_event(Event::BidSettled { - project_id, - account: bidder, - id: bid.id, - ct_amount: bid.final_ct_amount, - }); + + // If the multiplier is greater than 1, add the release schedule else release the held PLMC bond + if bid.multiplier.into() > 1u8 { + T::Vesting::add_release_schedule( + &bidder, + vest_info.total_amount, + vest_info.amount_per_block, + funding_end_block, + HoldReason::Participation(project_id).into(), + )?; + } else { + // Release the held PLMC bond + Self::release_bond(project_id, &bidder, bid.plmc_bond)?; + } + + // Mint the contribution tokens + Self::mint_ct_tokens(project_id, &bidder, bid.final_ct_amount)?; + + // Payout the bid funding asset amount to the project account + Self::release_funding_asset( + project_id, + &project_details.issuer_account, + bid.funding_asset_amount_locked, + bid.funding_asset, + )?; + + Self::create_migration( + project_id, + &bidder, + bid.id, + ParticipationType::Bid, + bid.final_ct_amount, + vest_info.duration, + )?; + + Bids::::remove((project_id, bidder.clone(), bid.id)); + + Self::deposit_event(Event::BidSettled { + project_id, + account: bidder, + id: bid.id, + ct_amount: bid.final_ct_amount, + }); Ok(()) - } + } - pub fn do_settle_failed_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!( - matches!(project_details.status, ProjectStatus::FundingFailed), - Error::::NotAllowed - ); + pub fn do_settle_failed_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::::NotAllowed); + + let bidder = bid.bidder; + + // Return the funding assets to the bidder + Self::release_funding_asset(project_id, &bidder, bid.funding_asset_amount_locked, bid.funding_asset)?; + + // Release the held PLMC bond + Self::release_bond(project_id, &bidder, bid.plmc_bond)?; + + // Remove the bid from the storage + Bids::::remove((project_id, bidder.clone(), bid.id)); + + Self::deposit_event(Event::BidSettled { project_id, account: bidder, id: bid.id, ct_amount: Zero::zero() }); + + Ok(()) + } - let bidder = bid.bidder; - - // Return the funding assets to the bidder - Self::release_funding_asset(project_id, &bidder, bid.funding_asset_amount_locked, bid.funding_asset)?; - - // Release the held PLMC bond - Self::release_bond(project_id, &bidder, bid.plmc_bond)?; - - // Remove the bid from the storage - Bids::::remove((project_id, bidder.clone(), bid.id)); - - Self::deposit_event(Event::BidSettled { - project_id, - account: bidder, - id: bid.id, - ct_amount: Zero::zero(), - }); - - Ok(()) - } - - pub fn do_settle_successful_contribution(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + pub fn do_settle_successful_contribution( + contribution: ContributionInfoOf, + project_id: ProjectId, + ) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; // Ensure that: - // 1. The project is in the FundingSuccessful state - // 2. The contribution token exists - ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); - ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); + // 1. The project is in the FundingSuccessful state + // 2. The contribution token exists + ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); + ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); + + let contributor = contribution.contributor; + + // Calculate the vesting info and add the release schedule + let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; + let vest_info = Self::calculate_vesting_info(&contributor, contribution.multiplier, contribution.plmc_bond) + .map_err(|_| Error::::BadMath)?; + + if contribution.multiplier.into() > 1u8 { + T::Vesting::add_release_schedule( + &contributor, + vest_info.total_amount, + vest_info.amount_per_block, + funding_end_block, + HoldReason::Participation(project_id).into(), + )?; + } else { + // Release the held PLMC bond + Self::release_bond(project_id, &contributor, contribution.plmc_bond)?; + } + + // Mint the contribution tokens + Self::mint_ct_tokens(project_id, &contributor, contribution.ct_amount)?; + + // Payout the bid funding asset amount to the project account + Self::release_funding_asset( + project_id, + &project_details.issuer_account, + contribution.funding_asset_amount, + contribution.funding_asset, + )?; - let contributor = contribution.contributor; + // Create Migration + Self::create_migration( + project_id, + &contributor, + contribution.id, + ParticipationType::Contribution, + contribution.ct_amount, + vest_info.duration, + )?; - // Calculate the vesting info and add the release schedule - let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; - let vest_info = - Self::calculate_vesting_info(&contributor, contribution.multiplier, contribution.plmc_bond).map_err(|_| Error::::BadMath)?; - - if contribution.multiplier.into() > 1u8 { - T::Vesting::add_release_schedule( - &contributor, - vest_info.total_amount, - vest_info.amount_per_block, - funding_end_block, - HoldReason::Participation(project_id).into(), - )?; - } else { - // Release the held PLMC bond - Self::release_bond(project_id, &contributor, contribution.plmc_bond)?; - } - - // Mint the contribution tokens - Self::mint_ct_tokens(project_id, &contributor, contribution.ct_amount)?; - - // Payout the bid funding asset amount to the project account - Self::release_funding_asset(project_id, &project_details.issuer_account, contribution.funding_asset_amount, contribution.funding_asset)?; - - // Create Migration - Self::create_migration(project_id, &contributor, contribution.id, ParticipationType::Contribution, contribution.ct_amount, vest_info.duration)?; - - Contributions::::remove((project_id, contributor.clone(), contribution.id)); - - Self::deposit_event(Event::ContributionSettled { - project_id, - account: contributor, - id: contribution.id, - ct_amount: contribution.ct_amount, - }); - - Ok(()) - } - - pub fn do_settle_failed_contribution(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!( - matches!(project_details.status, ProjectStatus::FundingFailed), - Error::::NotAllowed - ); + Contributions::::remove((project_id, contributor.clone(), contribution.id)); + + Self::deposit_event(Event::ContributionSettled { + project_id, + account: contributor, + id: contribution.id, + ct_amount: contribution.ct_amount, + }); + + Ok(()) + } + + pub fn do_settle_failed_contribution(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::::NotAllowed); + + // Check if the bidder has a future deposit held + let contributor = contribution.contributor; + + // Return the funding assets to the contributor + Self::release_funding_asset( + project_id, + &contributor, + contribution.funding_asset_amount, + contribution.funding_asset, + )?; + + // Release the held PLMC bond + Self::release_bond(project_id, &contributor, contribution.plmc_bond)?; - // Check if the bidder has a future deposit held - let contributor = contribution.contributor; - - - // Return the funding assets to the contributor - Self::release_funding_asset(project_id, &contributor, contribution.funding_asset_amount, contribution.funding_asset)?; - - // Release the held PLMC bond - Self::release_bond(project_id, &contributor, contribution.plmc_bond)?; - - - // Remove the bid from the storage - Contributions::::remove((project_id, contributor.clone(), contribution.id)); - - Self::deposit_event(Event::ContributionSettled { - project_id, - account: contributor, - id: contribution.id, - ct_amount: Zero::zero(), - }); - - Ok(()) - } - - - fn mint_ct_tokens(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { - if !T::ContributionTokenCurrency::contains(&project_id, participant) { - T::ContributionTokenCurrency::touch(project_id, participant, participant)?; - } - T::ContributionTokenCurrency::mint_into(project_id, participant, amount)?; - Ok(()) - } - - fn release_funding_asset(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf, asset: AcceptedFundingAsset) -> DispatchResult { - let project_pot = Self::fund_account_id(project_id); - T::FundingCurrency::transfer( - asset.to_assethub_id(), - &project_pot, - &participant, - amount, - Preservation::Expendable, - )?; - Ok(()) - } - - fn release_bond(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { - // Release the held PLMC bond - T::NativeCurrency::release( - &HoldReason::Participation(project_id).into(), - &participant, - amount, - Precision::Exact, - )?; - Ok(()) - } - - fn slash_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf) -> Result, DispatchError> { - - let slash_percentage = T::EvaluatorSlash::get(); + // Remove the bid from the storage + Contributions::::remove((project_id, contributor.clone(), contribution.id)); + + Self::deposit_event(Event::ContributionSettled { + project_id, + account: contributor, + id: contribution.id, + ct_amount: Zero::zero(), + }); + + Ok(()) + } + + fn mint_ct_tokens(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { + if !T::ContributionTokenCurrency::contains(&project_id, participant) { + T::ContributionTokenCurrency::touch(project_id, participant, participant)?; + } + T::ContributionTokenCurrency::mint_into(project_id, participant, amount)?; + Ok(()) + } + + fn release_funding_asset( + project_id: ProjectId, + participant: &AccountIdOf, + amount: BalanceOf, + asset: AcceptedFundingAsset, + ) -> DispatchResult { + let project_pot = Self::fund_account_id(project_id); + T::FundingCurrency::transfer( + asset.to_assethub_id(), + &project_pot, + &participant, + amount, + Preservation::Expendable, + )?; + Ok(()) + } + + fn release_bond(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { + // Release the held PLMC bond + T::NativeCurrency::release( + &HoldReason::Participation(project_id).into(), + &participant, + amount, + Precision::Exact, + )?; + Ok(()) + } + + fn slash_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf) -> Result, DispatchError> { + let slash_percentage = T::EvaluatorSlash::get(); let treasury_account = T::ProtocolGrowthTreasury::get(); // * Calculate variables * @@ -308,58 +336,55 @@ impl Pallet { Fortitude::Force, )?; - Ok(evaluation.current_plmc_bond.saturating_sub(slashed_amount)) - } - - fn reward_evaluator(project_id: ProjectId, evaluation: &EvaluationInfoOf, info: &RewardInfoOf) -> Result<(BalanceOf, BalanceOf), DispatchError> { - - - let reward = Self::calculate_evaluator_reward(evaluation, &info); - Self::mint_ct_tokens(project_id, &evaluation.evaluator, reward)?; - - Ok((evaluation.current_plmc_bond, reward)) - } - - pub fn calculate_evaluator_reward(evaluation: &EvaluationInfoOf, info: &RewardInfoOf) -> BalanceOf { - let early_reward_weight = Perquintill::from_rational( - evaluation.early_usd_amount, - info.early_evaluator_total_bonded_usd - ); - let normal_reward_weight = Perquintill::from_rational( - evaluation.late_usd_amount.saturating_add(evaluation.early_usd_amount), - info.normal_evaluator_total_bonded_usd, - ); - let early_evaluators_rewards = early_reward_weight * info.early_evaluator_reward_pot; - let normal_evaluators_rewards = normal_reward_weight * info.normal_evaluator_reward_pot; - early_evaluators_rewards.saturating_add(normal_evaluators_rewards) - } - - pub fn create_migration( - project_id: ProjectId, - origin: &AccountIdOf, - id: u32, - participation_type: ParticipationType, - ct_amount: BalanceOf, - vesting_time: BlockNumberFor, - ) -> DispatchResult { - UserMigrations::::try_mutate(project_id, origin, |maybe_migrations| -> DispatchResult { - let migration_origin = MigrationOrigin { - user: T::AccountId32Conversion::convert(origin.clone()), - id: id, - participation_type, - }; - let vesting_time: u64 = vesting_time.try_into().map_err(|_| Error::::BadMath)?; - let migration_info: MigrationInfo = (ct_amount.into(), vesting_time.into()).into(); - let migration = Migration::new(migration_origin, migration_info); - if let Some((_, migrations)) = maybe_migrations { - migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; - } else { - let mut migrations = BoundedVec::<_, MaxParticipationsPerUser>::new(); - migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; - *maybe_migrations = Some((MigrationStatus::NotStarted, migrations)) - } - - Ok(()) - }) - } -} \ No newline at end of file + Ok(evaluation.current_plmc_bond.saturating_sub(slashed_amount)) + } + + fn reward_evaluator( + project_id: ProjectId, + evaluation: &EvaluationInfoOf, + info: &RewardInfoOf, + ) -> Result<(BalanceOf, BalanceOf), DispatchError> { + let reward = Self::calculate_evaluator_reward(evaluation, &info); + Self::mint_ct_tokens(project_id, &evaluation.evaluator, reward)?; + + Ok((evaluation.current_plmc_bond, reward)) + } + + pub fn calculate_evaluator_reward(evaluation: &EvaluationInfoOf, info: &RewardInfoOf) -> BalanceOf { + let early_reward_weight = + Perquintill::from_rational(evaluation.early_usd_amount, info.early_evaluator_total_bonded_usd); + let normal_reward_weight = Perquintill::from_rational( + evaluation.late_usd_amount.saturating_add(evaluation.early_usd_amount), + info.normal_evaluator_total_bonded_usd, + ); + let early_evaluators_rewards = early_reward_weight * info.early_evaluator_reward_pot; + let normal_evaluators_rewards = normal_reward_weight * info.normal_evaluator_reward_pot; + early_evaluators_rewards.saturating_add(normal_evaluators_rewards) + } + + pub fn create_migration( + project_id: ProjectId, + origin: &AccountIdOf, + id: u32, + participation_type: ParticipationType, + ct_amount: BalanceOf, + vesting_time: BlockNumberFor, + ) -> DispatchResult { + UserMigrations::::try_mutate(project_id, origin, |maybe_migrations| -> DispatchResult { + let migration_origin = + MigrationOrigin { user: T::AccountId32Conversion::convert(origin.clone()), id, participation_type }; + let vesting_time: u64 = vesting_time.try_into().map_err(|_| Error::::BadMath)?; + let migration_info: MigrationInfo = (ct_amount.into(), vesting_time.into()).into(); + let migration = Migration::new(migration_origin, migration_info); + if let Some((_, migrations)) = maybe_migrations { + migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; + } else { + let mut migrations = BoundedVec::<_, MaxParticipationsPerUser>::new(); + migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; + *maybe_migrations = Some((MigrationStatus::NotStarted, migrations)) + } + + Ok(()) + }) + } +} diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index b97378fd3..8e8ffe5cc 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -1427,7 +1427,8 @@ mod auction { &rejected_user, HoldReason::Participation(project_id).into(), ) - }).is_none()); + }) + .is_none()); } // We use the already tested instantiator functions to calculate the correct post-wap returns @@ -3768,7 +3769,10 @@ mod remainder_contribution { mod funding_end_and_settlement { use super::*; - pub fn create_project_with_funding_percentage(percentage: u64, maybe_decision: Option) -> (MockInstantiator, ProjectId) { + pub fn create_project_with_funding_percentage( + percentage: u64, + maybe_decision: Option, + ) -> (MockInstantiator, ProjectId) { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(inst.get_new_nonce(), ISSUER_1); let min_price = project_metadata.minimum_price; @@ -3808,7 +3812,7 @@ mod funding_end_and_settlement { assert!(percentage <= 33); }, _ => panic!("unexpected project status"), - }; + }; inst.advance_time(::SuccessToSettlementTime::get() + 1u64).unwrap(); let funding_sucessful = match percentage { @@ -3840,35 +3844,37 @@ mod funding_end_and_settlement { assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); // execute `do_start_settlement` inst.advance_time(1).unwrap(); - // Settle the project. + // Settle the project. inst.settle_project(project_id).unwrap(); } #[test] fn automatic_fail_less_eq_33_percent() { for funding_percent in (1..=33).step_by(5) { - let _ = create_project_with_funding_percentage(funding_percent, None); + let _ = create_project_with_funding_percentage(funding_percent, None); } } #[test] fn automatic_success_bigger_eq_90_percent() { for funding_percent in (90..=100).step_by(2) { - let _ = create_project_with_funding_percentage(funding_percent, None); + let _ = create_project_with_funding_percentage(funding_percent, None); } } #[test] fn manual_acceptance_percentage_between_34_89() { for funding_percent in (34..=89).step_by(5) { - let _ = create_project_with_funding_percentage(funding_percent, Some(FundingOutcomeDecision::AcceptFunding)); + let _ = + create_project_with_funding_percentage(funding_percent, Some(FundingOutcomeDecision::AcceptFunding)); } } #[test] fn manual_rejection_percentage_between_34_89() { for funding_percent in (34..=89).step_by(5) { - let _ = create_project_with_funding_percentage(funding_percent, Some(FundingOutcomeDecision::RejectFunding)); + let _ = + create_project_with_funding_percentage(funding_percent, Some(FundingOutcomeDecision::RejectFunding)); } } @@ -3908,82 +3914,159 @@ mod funding_end_and_settlement { inst.settle_project(project_id).unwrap(); } - #[test] fn can_settle_accepted_project() { let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); + let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); let evaluations = inst.get_evaluations(project_id); let bids = inst.get_bids(project_id); let contributions = inst.get_contributions(project_id); - + inst.settle_project(project_id).unwrap(); inst.assert_evaluations_settled(project_id, evaluations, percentage); inst.assert_bids_settled(project_id, bids, true); - inst.assert_contributions_settled(project_id, contributions, true); + inst.assert_contributions_settled(project_id, contributions, true); } #[test] fn can_settle_failed_project() { let percentage = 33u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); + let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); let evaluations = inst.get_evaluations(project_id); let bids = inst.get_bids(project_id); let contributions = inst.get_contributions(project_id); - + inst.settle_project(project_id).unwrap(); inst.assert_evaluations_settled(project_id, evaluations, percentage); inst.assert_bids_settled(project_id, bids, false); - inst.assert_contributions_settled(project_id, contributions, false); + inst.assert_contributions_settled(project_id, contributions, false); } #[test] fn cannot_settle_successful_project_twice() { let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); - + let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); + let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); inst.execute(|| { let evaluator = first_evaluation.evaluator; - assert_ok!(crate::Pallet::::settle_successful_evaluation(RuntimeOrigin::signed(evaluator.clone()), project_id, evaluator, first_evaluation.id)); - assert_noop!(crate::Pallet::::settle_successful_evaluation(RuntimeOrigin::signed(evaluator.clone()), project_id, evaluator, first_evaluation.id), Error::::ParticipationNotFound); - + assert_ok!(crate::Pallet::::settle_successful_evaluation( + RuntimeOrigin::signed(evaluator.clone()), + project_id, + evaluator, + first_evaluation.id + )); + assert_noop!( + crate::Pallet::::settle_successful_evaluation( + RuntimeOrigin::signed(evaluator.clone()), + project_id, + evaluator, + first_evaluation.id + ), + Error::::ParticipationNotFound + ); + let bidder = first_bid.bidder; - assert_ok!(crate::Pallet::::settle_successful_bid(RuntimeOrigin::signed(bidder.clone()), project_id, bidder, first_bid.id)); - assert_noop!(crate::Pallet::::settle_successful_bid(RuntimeOrigin::signed(bidder.clone()), project_id, bidder, first_bid.id), Error::::ParticipationNotFound); + assert_ok!(crate::Pallet::::settle_successful_bid( + RuntimeOrigin::signed(bidder.clone()), + project_id, + bidder, + first_bid.id + )); + assert_noop!( + crate::Pallet::::settle_successful_bid( + RuntimeOrigin::signed(bidder.clone()), + project_id, + bidder, + first_bid.id + ), + Error::::ParticipationNotFound + ); let contributor = first_contribution.contributor; - assert_ok!(crate::Pallet::::settle_successful_contribution(RuntimeOrigin::signed(contributor.clone()), project_id, contributor, first_contribution.id)); - assert_noop!(crate::Pallet::::settle_successful_contribution(RuntimeOrigin::signed(contributor.clone()), project_id, contributor, first_contribution.id), Error::::ParticipationNotFound); + assert_ok!(crate::Pallet::::settle_successful_contribution( + RuntimeOrigin::signed(contributor.clone()), + project_id, + contributor, + first_contribution.id + )); + assert_noop!( + crate::Pallet::::settle_successful_contribution( + RuntimeOrigin::signed(contributor.clone()), + project_id, + contributor, + first_contribution.id + ), + Error::::ParticipationNotFound + ); }); } #[test] fn cannot_settle_failed_project_twice() { let percentage = 33u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); - + let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); + let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); inst.execute(|| { let evaluator = first_evaluation.evaluator; - assert_ok!(crate::Pallet::::settle_failed_evaluation(RuntimeOrigin::signed(evaluator.clone()), project_id, evaluator, first_evaluation.id)); - assert_noop!(crate::Pallet::::settle_failed_evaluation(RuntimeOrigin::signed(evaluator.clone()), project_id, evaluator, first_evaluation.id), Error::::ParticipationNotFound); - + assert_ok!(crate::Pallet::::settle_failed_evaluation( + RuntimeOrigin::signed(evaluator.clone()), + project_id, + evaluator, + first_evaluation.id + )); + assert_noop!( + crate::Pallet::::settle_failed_evaluation( + RuntimeOrigin::signed(evaluator.clone()), + project_id, + evaluator, + first_evaluation.id + ), + Error::::ParticipationNotFound + ); + let bidder = first_bid.bidder; - assert_ok!(crate::Pallet::::settle_failed_bid(RuntimeOrigin::signed(bidder.clone()), project_id, bidder, first_bid.id)); - assert_noop!(crate::Pallet::::settle_failed_bid(RuntimeOrigin::signed(bidder.clone()), project_id, bidder, first_bid.id), Error::::ParticipationNotFound); + assert_ok!(crate::Pallet::::settle_failed_bid( + RuntimeOrigin::signed(bidder.clone()), + project_id, + bidder, + first_bid.id + )); + assert_noop!( + crate::Pallet::::settle_failed_bid( + RuntimeOrigin::signed(bidder.clone()), + project_id, + bidder, + first_bid.id + ), + Error::::ParticipationNotFound + ); let contributor = first_contribution.contributor; - assert_ok!(crate::Pallet::::settle_failed_contribution(RuntimeOrigin::signed(contributor.clone()), project_id, contributor, first_contribution.id)); - assert_noop!(crate::Pallet::::settle_failed_contribution(RuntimeOrigin::signed(contributor.clone()), project_id, contributor, first_contribution.id), Error::::ParticipationNotFound); + assert_ok!(crate::Pallet::::settle_failed_contribution( + RuntimeOrigin::signed(contributor.clone()), + project_id, + contributor, + first_contribution.id + )); + assert_noop!( + crate::Pallet::::settle_failed_contribution( + RuntimeOrigin::signed(contributor.clone()), + project_id, + contributor, + first_contribution.id + ), + Error::::ParticipationNotFound + ); }); } @@ -3992,7 +4075,7 @@ mod funding_end_and_settlement { #[test] fn evaluator_slashed_if_between_33_and_75() { let percentage = 50u64; - let project_1 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding)); + let project_1 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding)); let project_2 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding)); let projects = vec![project_1, project_2]; @@ -4004,15 +4087,30 @@ mod funding_end_and_settlement { let prev_balance = ::NativeCurrency::balance(&evaluator); match ProjectsDetails::::get(project_id).unwrap().status { ProjectStatus::FundingSuccessful => { - assert_ok!(crate::Pallet::::settle_successful_evaluation(RuntimeOrigin::signed(evaluator.clone()), project_id, evaluator, first_evaluation.id)); + assert_ok!(crate::Pallet::::settle_successful_evaluation( + RuntimeOrigin::signed(evaluator.clone()), + project_id, + evaluator, + first_evaluation.id + )); }, ProjectStatus::FundingFailed => { - assert_ok!(crate::Pallet::::settle_failed_evaluation(RuntimeOrigin::signed(evaluator.clone()), project_id, evaluator, first_evaluation.id)); + assert_ok!(crate::Pallet::::settle_failed_evaluation( + RuntimeOrigin::signed(evaluator.clone()), + project_id, + evaluator, + first_evaluation.id + )); }, _ => panic!("unexpected project status"), } let balance = ::NativeCurrency::balance(&evaluator); - assert_eq!(balance, prev_balance + (Percent::from_percent(100) - ::EvaluatorSlash::get()) * first_evaluation.current_plmc_bond); + assert_eq!( + balance, + prev_balance + + (Percent::from_percent(100) - ::EvaluatorSlash::get()) * + first_evaluation.current_plmc_bond + ); }); } } @@ -4022,7 +4120,7 @@ mod funding_end_and_settlement { #[test] fn evaluator_plmc_unchanged_between_76_and_89() { let percentage = 80u64; - let project_1 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding)); + let project_1 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding)); let project_2 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding)); let projects = vec![project_1, project_2]; @@ -4034,10 +4132,20 @@ mod funding_end_and_settlement { let prev_balance = ::NativeCurrency::balance(&evaluator); match ProjectsDetails::::get(project_id).unwrap().status { ProjectStatus::FundingSuccessful => { - assert_ok!(crate::Pallet::::settle_successful_evaluation(RuntimeOrigin::signed(evaluator.clone()), project_id, evaluator, first_evaluation.id)); + assert_ok!(crate::Pallet::::settle_successful_evaluation( + RuntimeOrigin::signed(evaluator.clone()), + project_id, + evaluator, + first_evaluation.id + )); }, ProjectStatus::FundingFailed => { - assert_ok!(crate::Pallet::::settle_failed_evaluation(RuntimeOrigin::signed(evaluator.clone()), project_id, evaluator, first_evaluation.id)); + assert_ok!(crate::Pallet::::settle_failed_evaluation( + RuntimeOrigin::signed(evaluator.clone()), + project_id, + evaluator, + first_evaluation.id + )); }, _ => panic!("unexpected project status"), } @@ -4050,19 +4158,25 @@ mod funding_end_and_settlement { #[test] fn bid_is_correctly_settled_for_successful_project() { let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); + let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); let issuer = &inst.get_issuer(project_id); inst.execute(|| { let bidder = first_bid.bidder; - assert_ok!(crate::Pallet::::settle_successful_bid(RuntimeOrigin::signed(bidder.clone()), project_id, bidder, first_bid.id)); - + assert_ok!(crate::Pallet::::settle_successful_bid( + RuntimeOrigin::signed(bidder.clone()), + project_id, + bidder, + first_bid.id + )); + let reason: RuntimeHoldReason = HoldReason::Participation(project_id).into(); let held_bidder = ::NativeCurrency::balance_on_hold(&reason, &bidder); assert_eq!(held_bidder, 0u32.into()); - let balance_issuer = ::FundingCurrency::balance(first_bid.funding_asset.to_assethub_id(), issuer); + let balance_issuer = + ::FundingCurrency::balance(first_bid.funding_asset.to_assethub_id(), issuer); assert_eq!(balance_issuer, first_bid.funding_asset_amount_locked); let ct_amount = ::ContributionTokenCurrency::balance(project_id, &bidder); @@ -4073,18 +4187,24 @@ mod funding_end_and_settlement { #[test] fn bid_is_correctly_settled_for_failed_project() { let percentage = 33u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); + let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); inst.execute(|| { let bidder = first_bid.bidder; - assert_ok!(crate::Pallet::::settle_failed_bid(RuntimeOrigin::signed(bidder.clone()), project_id, bidder, first_bid.id)); - + assert_ok!(crate::Pallet::::settle_failed_bid( + RuntimeOrigin::signed(bidder.clone()), + project_id, + bidder, + first_bid.id + )); + let reason: RuntimeHoldReason = HoldReason::Participation(project_id).into(); let held_bidder = ::NativeCurrency::balance_on_hold(&reason, &bidder); assert_eq!(held_bidder, 0u32.into()); - let funding_asset_bidder = ::FundingCurrency::balance(first_bid.funding_asset.to_assethub_id(), &bidder); + let funding_asset_bidder = + ::FundingCurrency::balance(first_bid.funding_asset.to_assethub_id(), &bidder); assert_eq!(funding_asset_bidder, first_bid.funding_asset_amount_locked); let ct_amount = ::ContributionTokenCurrency::balance(project_id, &bidder); @@ -4095,19 +4215,27 @@ mod funding_end_and_settlement { #[test] fn contribution_is_correctly_settled_for_successful_project() { let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); + let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); let issuer = &inst.get_issuer(project_id); inst.execute(|| { let contributor = first_contribution.contributor; - assert_ok!(crate::Pallet::::settle_successful_contribution(RuntimeOrigin::signed(contributor.clone()), project_id, contributor, first_contribution.id)); - + assert_ok!(crate::Pallet::::settle_successful_contribution( + RuntimeOrigin::signed(contributor.clone()), + project_id, + contributor, + first_contribution.id + )); + let reason: RuntimeHoldReason = HoldReason::Participation(project_id).into(); let held_contributor = ::NativeCurrency::balance_on_hold(&reason, &contributor); assert_eq!(held_contributor, 0u32.into()); - let balance_issuer = ::FundingCurrency::balance(first_contribution.funding_asset.to_assethub_id(), issuer); + let balance_issuer = ::FundingCurrency::balance( + first_contribution.funding_asset.to_assethub_id(), + issuer, + ); assert_eq!(balance_issuer, first_contribution.usd_contribution_amount); let ct_amount = ::ContributionTokenCurrency::balance(project_id, &contributor); @@ -4118,27 +4246,32 @@ mod funding_end_and_settlement { #[test] fn contribution_is_correctly_settled_for_failed_project() { let percentage = 33u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); + let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None); let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); inst.execute(|| { let contributor = first_contribution.contributor; - assert_ok!(crate::Pallet::::settle_failed_contribution(RuntimeOrigin::signed(contributor.clone()), project_id, contributor, first_contribution.id)); - + assert_ok!(crate::Pallet::::settle_failed_contribution( + RuntimeOrigin::signed(contributor.clone()), + project_id, + contributor, + first_contribution.id + )); + let reason: RuntimeHoldReason = HoldReason::Participation(project_id).into(); let held_contributor = ::NativeCurrency::balance_on_hold(&reason, &contributor); assert_eq!(held_contributor, 0u32.into()); - let funding_asset_contributor = ::FundingCurrency::balance(first_contribution.funding_asset.to_assethub_id(), &contributor); + let funding_asset_contributor = ::FundingCurrency::balance( + first_contribution.funding_asset.to_assethub_id(), + &contributor, + ); assert_eq!(funding_asset_contributor, first_contribution.usd_contribution_amount); let ct_amount = ::ContributionTokenCurrency::balance(project_id, &contributor); assert_eq!(ct_amount, Zero::zero()); }); } - - - } // only functionalities related to the CT Migration diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index dee761425..e5db213e0 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -306,15 +306,7 @@ pub mod storage_types { } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] - pub struct BidInfo< - ProjectId, - Did, - Balance: BalanceT, - Price: FixedPointNumber, - AccountId, - BlockNumber, - Multiplier, - > { + pub struct BidInfo { pub id: u32, pub project_id: ProjectId, pub bidder: AccountId, @@ -785,4 +777,4 @@ pub mod inner_types { pub project_id: ProjectId, pub migration_origins: MigrationOrigins, } -} \ No newline at end of file +} diff --git a/polimec-common/common/src/lib.rs b/polimec-common/common/src/lib.rs index a2f05db34..7cabd2a2a 100644 --- a/polimec-common/common/src/lib.rs +++ b/polimec-common/common/src/lib.rs @@ -154,7 +154,7 @@ pub mod migration_types { pub origin: MigrationOrigin, pub info: MigrationInfo, } - + impl Migration { pub fn new(origin: MigrationOrigin, info: MigrationInfo) -> Self { Self { origin, info } From 737e379e4d311e5de3cf54996306242cee51696a Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Tue, 26 Mar 2024 17:08:41 +0100 Subject: [PATCH 16/21] fix test + implement all participation settled check + fmt --- integration-tests/penpal/src/lib.rs | 1 - integration-tests/penpal/src/weights/mod.rs | 1 - integration-tests/src/tests/ct_migration.rs | 25 +++------------------ pallets/funding/src/benchmarking.rs | 4 +++- pallets/funding/src/functions.rs | 11 ++++++++- pallets/funding/src/tests.rs | 2 +- 6 files changed, 17 insertions(+), 27 deletions(-) diff --git a/integration-tests/penpal/src/lib.rs b/integration-tests/penpal/src/lib.rs index 1ae1b7f11..970b66109 100644 --- a/integration-tests/penpal/src/lib.rs +++ b/integration-tests/penpal/src/lib.rs @@ -82,7 +82,6 @@ use parachains_common::{ }; use sp_runtime::traits::Convert; use xcm::latest::prelude::BodyId; -use xcm_executor::XcmExecutor; /// Balance of an account. pub type Balance = u128; diff --git a/integration-tests/penpal/src/weights/mod.rs b/integration-tests/penpal/src/weights/mod.rs index ed0b4dbcd..ef1ab0d1a 100644 --- a/integration-tests/penpal/src/weights/mod.rs +++ b/integration-tests/penpal/src/weights/mod.rs @@ -24,5 +24,4 @@ pub mod rocksdb_weights; pub use block_weights::constants::BlockExecutionWeight; pub use extrinsic_weights::constants::ExtrinsicBaseWeight; -pub use paritydb_weights::constants::ParityDbWeight; pub use rocksdb_weights::constants::RocksDbWeight; diff --git a/integration-tests/src/tests/ct_migration.rs b/integration-tests/src/tests/ct_migration.rs index 0082101a2..42667e57e 100644 --- a/integration-tests/src/tests/ct_migration.rs +++ b/integration-tests/src/tests/ct_migration.rs @@ -36,6 +36,9 @@ fn mock_hrmp_establishment(project_id: u32) { let channel_accepted_message = xcm::v3::opaque::Instruction::HrmpChannelAccepted { recipient: 6969u32 }; assert_ok!(PolimecFunding::do_handle_channel_accepted(channel_accepted_message)); }); + + // Required for passing migration ready check. + PenNet::execute_with(|| {}); } fn assert_migration_is_ready(project_id: u32) { @@ -78,15 +81,6 @@ fn migrations_are_executed(project_id: ProjectId, accounts: Vec) { let user_info = PenNet::account_data_of(account.clone()); PenNet::execute_with(|| { let (_, migrations) = user_migrations.get(&account).unwrap(); - let matched_events = PenNet::events() - .iter() - .filter(|event| match event { - PenpalEvent::PolimecReceiver(polimec_receiver::Event::MigrationExecuted { migration }) => - migrations.contains(&migration), - _ => false, - }) - .count(); - assert_eq!(matched_events, migrations.len()); assert_close_enough!(user_info.free, migrations.total_ct_amount(), Perquintill::from_float(0.99)); @@ -113,19 +107,6 @@ fn migrations_are_confirmed(project_id: u32, accounts: Vec) { for user in accounts.iter() { let (current_status, _) = user_migrations.get(user).unwrap(); assert_eq!(current_status, &MigrationStatus::Confirmed); - - let matched_events: usize = PolitestNet::events() - .iter() - .filter(|event| match event { - PolitestEvent::PolimecFunding(pallet_funding::Event::MigrationStatusUpdated { - project_id, - account, - status, - }) => project_id == project_id && account == user && matches!(status, &MigrationStatus::Confirmed), - _ => false, - }) - .count(); - assert_eq!(matched_events, 1); } }); } diff --git a/pallets/funding/src/benchmarking.rs b/pallets/funding/src/benchmarking.rs index e9d3a047a..4736f96f7 100644 --- a/pallets/funding/src/benchmarking.rs +++ b/pallets/funding/src/benchmarking.rs @@ -549,7 +549,9 @@ mod benchmarks { assert_eq!(stored_metadata, project_metadata); // Events - frame_system::Pallet::::assert_last_event(Event::::MetadataEdited { project_id, metadata: project_metadata }.into()); + frame_system::Pallet::::assert_last_event( + Event::::MetadataEdited { project_id, metadata: project_metadata }.into(), + ); } #[benchmark] diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index bd927724d..3653eb939 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -18,6 +18,7 @@ //! Functions for the Funding pallet. use crate::ProjectStatus::FundingSuccessful; +use core::ops::Not; use frame_support::{ dispatch::{DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, PostDispatchInfo}, ensure, @@ -37,7 +38,6 @@ use sp_arithmetic::{ Percent, Perquintill, }; use sp_runtime::traits::Convert; -use core::ops::Not; use super::*; use crate::traits::{BondingRequirementCalculation, ProvideAssetPrice, VestingDurationCalculation}; @@ -1819,6 +1819,7 @@ impl Pallet { let migration_readiness_check = project_details.migration_readiness_check.ok_or(Error::::NotAllowed)?; let project_para_id = project_details.parachain_id.ok_or(Error::::ImpossibleState)?; let now = >::block_number(); + ensure!(Self::user_has_no_participations(project_id, participant.clone()), Error::::NotAllowed); let (_, migrations) = UserMigrations::::get(project_id, participant.clone()).ok_or(Error::::NoMigrationsFound)?; @@ -2422,6 +2423,14 @@ impl Pallet { available_bytes_for_migration_per_message.saturating_div(one_migration_bytes) } + pub fn user_has_no_participations(project_id: ProjectId, user: AccountIdOf) -> bool { + Evaluations::::iter_prefix_values((project_id, user.clone())) + .map(|eval| eval.id) + .chain(Bids::::iter_prefix_values((project_id, user.clone())).map(|bid| bid.id)) + .chain(Contributions::::iter_prefix_values((project_id, user)).map(|contribution| contribution.id)) + .count() == 0 + } + pub fn construct_migration_xcm_message(migrations: Vec, query_id: QueryId) -> Xcm<()> { // TODO: adjust this as benchmarks for polimec-receiver are written const MAX_WEIGHT: Weight = Weight::from_parts(10_000, 0); diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index ac0f90662..d7959fffa 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -5102,7 +5102,7 @@ mod funding_end_and_settlement { let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); inst.execute(|| { let contributor = first_contribution.contributor; - + assert_ok!(crate::Pallet::::settle_failed_contribution( RuntimeOrigin::signed(contributor.clone()), project_id, From 71671491c4139838781259f69c367eeddf10f09d Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Tue, 2 Apr 2024 10:32:41 +0200 Subject: [PATCH 17/21] address feedback --- integration-tests/src/tests/ct_migration.rs | 19 +++-- pallets/funding/src/functions.rs | 20 +++-- pallets/funding/src/instantiator.rs | 88 ++++++++------------- pallets/funding/src/lib.rs | 10 ++- pallets/funding/src/settlement.rs | 23 +++--- pallets/funding/src/tests.rs | 14 +--- 6 files changed, 76 insertions(+), 98 deletions(-) diff --git a/integration-tests/src/tests/ct_migration.rs b/integration-tests/src/tests/ct_migration.rs index 42667e57e..114cbc5b1 100644 --- a/integration-tests/src/tests/ct_migration.rs +++ b/integration-tests/src/tests/ct_migration.rs @@ -138,7 +138,7 @@ fn migrations_are_vested(project_id: u32, accounts: Vec) { }); } -fn create_settled_project() -> ProjectId { +fn create_settled_project() -> (ProjectId, Vec) { let mut inst = IntegrationInstantiator::new(None); PolitestNet::execute_with(|| { let project_id = inst.create_finished_project( @@ -150,19 +150,22 @@ fn create_settled_project() -> ProjectId { default_remainder_contributions(), ); inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.settle_project(project_id).unwrap(); - project_id + let mut participants: Vec = pallet_funding::Evaluations::::iter_prefix_values((project_id,)).map(|eval| eval.evaluator) + .chain(pallet_funding::Bids::::iter_prefix_values((project_id,)).map(|bid| bid.bidder)) + .chain(pallet_funding::Contributions::::iter_prefix_values((project_id,)).map(|contribution| contribution.contributor)).collect(); + participants.sort(); + participants.dedup(); + + inst.settle_project(project_id).unwrap(); + (project_id, participants) }) } #[test] fn full_migration_test() { - let project_id = create_settled_project(); + let (project_id, participants) = create_settled_project(); - let participants = vec![EVAL_1, EVAL_2, EVAL_3, BIDDER_1, BIDDER_2, BUYER_1, BUYER_2] - .into_iter() - .map(|x| AccountId::from(x)) - .collect::>(); + dbg!(get_migrations_for_participants(project_id, participants.clone())); mock_hrmp_establishment(project_id); diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index 3653eb939..a52d66115 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -2136,6 +2136,12 @@ impl Pallet { Precision::Exact, )?; + // Refund bid should only be called when the bid is rejected, so this if let should + // always match. + if let BidStatus::Rejected(reason) = bid.status { + Self::deposit_event(Event::BidRefunded { project_id, account: bid.bidder.clone(), bid_id: bid.id, reason }); + } + Ok(()) } @@ -2423,15 +2429,14 @@ impl Pallet { available_bytes_for_migration_per_message.saturating_div(one_migration_bytes) } + /// Check if the user has no participations (left) in the project. pub fn user_has_no_participations(project_id: ProjectId, user: AccountIdOf) -> bool { - Evaluations::::iter_prefix_values((project_id, user.clone())) - .map(|eval| eval.id) - .chain(Bids::::iter_prefix_values((project_id, user.clone())).map(|bid| bid.id)) - .chain(Contributions::::iter_prefix_values((project_id, user)).map(|contribution| contribution.id)) - .count() == 0 + Evaluations::::iter_prefix_values((project_id, user.clone())).next().is_none() + && Bids::::iter_prefix_values((project_id, user.clone())).next().is_none() + && Contributions::::iter_prefix_values((project_id, user)).next().is_none() } - pub fn construct_migration_xcm_message(migrations: Vec, query_id: QueryId) -> Xcm<()> { + pub fn construct_migration_xcm_message(migrations: BoundedVec>, query_id: QueryId) -> Xcm<()> { // TODO: adjust this as benchmarks for polimec-receiver are written const MAX_WEIGHT: Weight = Weight::from_parts(10_000, 0); const MAX_RESPONSE_WEIGHT: Weight = Weight::from_parts(700_000_000, 10_000); @@ -2440,6 +2445,9 @@ impl Pallet { let migrations_item = Migrations::from(migrations.into()); let mut encoded_call = vec![51u8, 0]; + // migrations_item can contain a Maximum of MaxParticipationsPerUser migrations which + // is 48. So we know that there is an upper limit to this encoded call, namely 48 * + // Migration encode size. encoded_call.extend_from_slice(migrations_item.encode().as_slice()); Xcm(vec![ UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index ee1974e1b..562a0901e 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1409,30 +1409,16 @@ impl< ); assert_eq!(reserved, 0u64.into()); - match percentage { + let (amount, should_exist) = match percentage { 0..=75 => { assert!(::NativeCurrency::balance(&account) < evaluation.current_plmc_bond); assert!(matches!(reward_info, EvaluatorsOutcome::Slashed)); - Self::assert_migration( - project_id, - account, - 0u64.into(), - evaluation.id, - ParticipationType::Evaluation, - false, - ); + (0u64.into(), false) }, 76..=89 => { assert!(::NativeCurrency::balance(&account) >= evaluation.current_plmc_bond); assert!(matches!(reward_info, EvaluatorsOutcome::Unchanged)); - Self::assert_migration( - project_id, - account, - 0u64.into(), - evaluation.id, - ParticipationType::Evaluation, - false, - ); + (0u64.into(), false) }, 90..=100 => { assert!(::NativeCurrency::balance(&account) >= evaluation.current_plmc_bond); @@ -1441,17 +1427,18 @@ impl< Pallet::::calculate_evaluator_reward(&evaluation, &info), _ => panic!("Evaluators should be rewarded"), }; - Self::assert_migration( - project_id, - account, - reward, - evaluation.id, - ParticipationType::Evaluation, - true, - ); + (reward, true) }, _ => panic!("Percentage should be between 0 and 100"), - } + }; + Self::assert_migration( + project_id, + account, + amount, + evaluation.id, + ParticipationType::Evaluation, + should_exist, + ); } }); } @@ -1462,18 +1449,15 @@ impl< for bid in bids { let account = bid.bidder.clone(); assert_eq!(Bids::::iter_prefix_values((&project_id, &account)).count(), 0); - if is_successful { - Self::assert_migration( - project_id, - account, - bid.final_ct_amount, - bid.id, - ParticipationType::Bid, - true, - ); - } else { - Self::assert_migration(project_id, account, 0u64.into(), bid.id, ParticipationType::Bid, false); - } + let amount: BalanceOf = if is_successful { bid.final_ct_amount } else { 0u64.into() }; + Self::assert_migration( + project_id, + account, + amount, + bid.id, + ParticipationType::Bid, + is_successful, + ); } }); } @@ -1489,25 +1473,15 @@ impl< for contribution in contributions { let account = contribution.contributor.clone(); assert_eq!(Bids::::iter_prefix_values((&project_id, &account)).count(), 0); - if is_successful { - Self::assert_migration( - project_id, - account, - contribution.ct_amount, - contribution.id, - ParticipationType::Contribution, - true, - ); - } else { - Self::assert_migration( - project_id, - account, - 0u64.into(), - contribution.id, - ParticipationType::Contribution, - false, - ); - } + let amount: BalanceOf = if is_successful { contribution.ct_amount } else { 0u64.into() }; + Self::assert_migration( + project_id, + account, + amount, + contribution.id, + ParticipationType::Contribution, + is_successful + ); } }); } diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index ccf42a12b..7d8c50288 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -188,9 +188,7 @@ pub mod pallet { use super::*; use crate::traits::{BondingRequirementCalculation, ProvideAssetPrice, VestingDurationCalculation}; use frame_support::{ - dispatch::{GetDispatchInfo, PostDispatchInfo}, - pallet_prelude::*, - traits::{OnFinalize, OnIdle, OnInitialize}, + dispatch::{GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, traits::{OnFinalize, OnIdle, OnInitialize} }; use frame_system::pallet_prelude::*; use sp_arithmetic::Percent; @@ -669,6 +667,12 @@ pub mod pallet { ct_amount: BalanceOf, slashed_amount: BalanceOf, }, + BidRefunded { + project_id: ProjectId, + account: AccountIdOf, + bid_id: u32, + reason: RejectionReason, + }, BidSettled { project_id: ProjectId, account: AccountIdOf, diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index 3c4243694..eb58a8ad8 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -106,10 +106,7 @@ impl Pallet { pub fn do_settle_successful_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - // Ensure that: - // 1. The project is in the FundingSuccessful state - // 2. The bid is in the Accepted or PartiallyAccepted state - // 3. The contribution token exists + ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); ensure!(matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), Error::::NotAllowed); ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); @@ -132,11 +129,11 @@ impl Pallet { )?; } else { // Release the held PLMC bond - Self::release_bond(project_id, &bidder, bid.plmc_bond)?; + Self::release_participation_bond(project_id, &bidder, bid.plmc_bond)?; } // Mint the contribution tokens - Self::mint_ct_tokens(project_id, &bidder, bid.final_ct_amount)?; + Self::mint_contribution_tokens(project_id, &bidder, bid.final_ct_amount)?; // Payout the bid funding asset amount to the project account Self::release_funding_asset( @@ -177,7 +174,7 @@ impl Pallet { Self::release_funding_asset(project_id, &bidder, bid.funding_asset_amount_locked, bid.funding_asset)?; // Release the held PLMC bond - Self::release_bond(project_id, &bidder, bid.plmc_bond)?; + Self::release_participation_bond(project_id, &bidder, bid.plmc_bond)?; // Remove the bid from the storage Bids::::remove((project_id, bidder.clone(), bid.id)); @@ -215,11 +212,11 @@ impl Pallet { )?; } else { // Release the held PLMC bond - Self::release_bond(project_id, &contributor, contribution.plmc_bond)?; + Self::release_participation_bond(project_id, &contributor, contribution.plmc_bond)?; } // Mint the contribution tokens - Self::mint_ct_tokens(project_id, &contributor, contribution.ct_amount)?; + Self::mint_contribution_tokens(project_id, &contributor, contribution.ct_amount)?; // Payout the bid funding asset amount to the project account Self::release_funding_asset( @@ -267,7 +264,7 @@ impl Pallet { )?; // Release the held PLMC bond - Self::release_bond(project_id, &contributor, contribution.plmc_bond)?; + Self::release_participation_bond(project_id, &contributor, contribution.plmc_bond)?; // Remove the bid from the storage Contributions::::remove((project_id, contributor.clone(), contribution.id)); @@ -282,7 +279,7 @@ impl Pallet { Ok(()) } - fn mint_ct_tokens(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { + fn mint_contribution_tokens(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { if !T::ContributionTokenCurrency::contains(&project_id, participant) { T::ContributionTokenCurrency::touch(project_id, participant, participant)?; } @@ -307,7 +304,7 @@ impl Pallet { Ok(()) } - fn release_bond(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { + fn release_participation_bond(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { // Release the held PLMC bond T::NativeCurrency::release( &HoldReason::Participation(project_id).into(), @@ -345,7 +342,7 @@ impl Pallet { info: &RewardInfoOf, ) -> Result<(BalanceOf, BalanceOf), DispatchError> { let reward = Self::calculate_evaluator_reward(evaluation, &info); - Self::mint_ct_tokens(project_id, &evaluation.evaluator, reward)?; + Self::mint_contribution_tokens(project_id, &evaluation.evaluator, reward)?; Ok((evaluation.current_plmc_bond, reward)) } diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index d7959fffa..25c3b78fd 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -2431,7 +2431,6 @@ mod auction { // only functionalities that happen in the COMMUNITY FUNDING period of a project mod community_contribution { use super::*; - use std::collections::HashMap; pub const HOURS: BlockNumber = 300u64; #[test] @@ -3598,7 +3597,7 @@ mod community_contribution { default_bids(), ) }; - let mut contribute = |inst: &mut MockInstantiator, project_id, multiplier| { + let contribute = |inst: &mut MockInstantiator, project_id, multiplier| { let jwt = get_mock_jwt(BUYER_1, InvestorType::Retail, generate_did_from_account(BUYER_1)); let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); let contributor_plmc = MockInstantiator::calculate_contributed_plmc_spent( @@ -4540,7 +4539,7 @@ mod remainder_contribution { vec![], ) }; - let mut contribute = |inst: &mut MockInstantiator, project_id, multiplier| { + let contribute = |inst: &mut MockInstantiator, project_id, multiplier| { let jwt = get_mock_jwt(BUYER_1, InvestorType::Retail, generate_did_from_account(BUYER_1)); let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); let contributor_plmc = MockInstantiator::calculate_contributed_plmc_spent( @@ -5298,11 +5297,7 @@ mod helper_functions { funding_destination_account: ISSUER_1, offchain_information_hash: Some(hashed(METADATA)), }; - let plmc_charged = MockInstantiator::calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - ); + let project_id = inst.create_community_contributing_project( project_metadata.clone(), ISSUER_1, @@ -5310,9 +5305,6 @@ mod helper_functions { bids.clone(), ); - let stored_bids = inst.execute(|| { - Bids::::iter_values().into_iter().sorted_by(|b1, b2| b1.id.cmp(&b2.id)).collect_vec() - }); let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); let expected_returns = vec![ From 488b71197f6c7c6e4aaac12b9349b2c7a10b4ec3 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Tue, 2 Apr 2024 10:32:58 +0200 Subject: [PATCH 18/21] fmt --- integration-tests/src/tests/ct_migration.rs | 14 ++++++++++---- pallets/funding/src/functions.rs | 11 +++++++---- pallets/funding/src/instantiator.rs | 11 ++--------- pallets/funding/src/lib.rs | 4 +++- pallets/funding/src/settlement.rs | 12 ++++++++++-- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/integration-tests/src/tests/ct_migration.rs b/integration-tests/src/tests/ct_migration.rs index 114cbc5b1..7dfd5d5b4 100644 --- a/integration-tests/src/tests/ct_migration.rs +++ b/integration-tests/src/tests/ct_migration.rs @@ -150,13 +150,19 @@ fn create_settled_project() -> (ProjectId, Vec) { default_remainder_contributions(), ); inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - let mut participants: Vec = pallet_funding::Evaluations::::iter_prefix_values((project_id,)).map(|eval| eval.evaluator) - .chain(pallet_funding::Bids::::iter_prefix_values((project_id,)).map(|bid| bid.bidder)) - .chain(pallet_funding::Contributions::::iter_prefix_values((project_id,)).map(|contribution| contribution.contributor)).collect(); + let mut participants: Vec = + pallet_funding::Evaluations::::iter_prefix_values((project_id,)) + .map(|eval| eval.evaluator) + .chain(pallet_funding::Bids::::iter_prefix_values((project_id,)).map(|bid| bid.bidder)) + .chain( + pallet_funding::Contributions::::iter_prefix_values((project_id,)) + .map(|contribution| contribution.contributor), + ) + .collect(); participants.sort(); participants.dedup(); - inst.settle_project(project_id).unwrap(); + inst.settle_project(project_id).unwrap(); (project_id, participants) }) } diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index a52d66115..99bdcf67b 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -2431,12 +2431,15 @@ impl Pallet { /// Check if the user has no participations (left) in the project. pub fn user_has_no_participations(project_id: ProjectId, user: AccountIdOf) -> bool { - Evaluations::::iter_prefix_values((project_id, user.clone())).next().is_none() - && Bids::::iter_prefix_values((project_id, user.clone())).next().is_none() - && Contributions::::iter_prefix_values((project_id, user)).next().is_none() + Evaluations::::iter_prefix_values((project_id, user.clone())).next().is_none() && + Bids::::iter_prefix_values((project_id, user.clone())).next().is_none() && + Contributions::::iter_prefix_values((project_id, user)).next().is_none() } - pub fn construct_migration_xcm_message(migrations: BoundedVec>, query_id: QueryId) -> Xcm<()> { + pub fn construct_migration_xcm_message( + migrations: BoundedVec>, + query_id: QueryId, + ) -> Xcm<()> { // TODO: adjust this as benchmarks for polimec-receiver are written const MAX_WEIGHT: Weight = Weight::from_parts(10_000, 0); const MAX_RESPONSE_WEIGHT: Weight = Weight::from_parts(700_000_000, 10_000); diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index 562a0901e..44bf064a0 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1450,14 +1450,7 @@ impl< let account = bid.bidder.clone(); assert_eq!(Bids::::iter_prefix_values((&project_id, &account)).count(), 0); let amount: BalanceOf = if is_successful { bid.final_ct_amount } else { 0u64.into() }; - Self::assert_migration( - project_id, - account, - amount, - bid.id, - ParticipationType::Bid, - is_successful, - ); + Self::assert_migration(project_id, account, amount, bid.id, ParticipationType::Bid, is_successful); } }); } @@ -1480,7 +1473,7 @@ impl< amount, contribution.id, ParticipationType::Contribution, - is_successful + is_successful, ); } }); diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 7d8c50288..a86a99479 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -188,7 +188,9 @@ pub mod pallet { use super::*; use crate::traits::{BondingRequirementCalculation, ProvideAssetPrice, VestingDurationCalculation}; use frame_support::{ - dispatch::{GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, traits::{OnFinalize, OnIdle, OnInitialize} + dispatch::{GetDispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + traits::{OnFinalize, OnIdle, OnInitialize}, }; use frame_system::pallet_prelude::*; use sp_arithmetic::Percent; diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index eb58a8ad8..617c1cab5 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -279,7 +279,11 @@ impl Pallet { Ok(()) } - fn mint_contribution_tokens(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { + fn mint_contribution_tokens( + project_id: ProjectId, + participant: &AccountIdOf, + amount: BalanceOf, + ) -> DispatchResult { if !T::ContributionTokenCurrency::contains(&project_id, participant) { T::ContributionTokenCurrency::touch(project_id, participant, participant)?; } @@ -304,7 +308,11 @@ impl Pallet { Ok(()) } - fn release_participation_bond(project_id: ProjectId, participant: &AccountIdOf, amount: BalanceOf) -> DispatchResult { + fn release_participation_bond( + project_id: ProjectId, + participant: &AccountIdOf, + amount: BalanceOf, + ) -> DispatchResult { // Release the held PLMC bond T::NativeCurrency::release( &HoldReason::Participation(project_id).into(), From fb2058683ccbc96e90c48b521b860d64e33195bd Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Tue, 2 Apr 2024 12:14:22 +0200 Subject: [PATCH 19/21] rename instantiator functions --- pallets/funding/src/instantiator.rs | 14 +++----------- pallets/funding/src/tests.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index 44bf064a0..5383f10a1 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1385,7 +1385,7 @@ impl< // Used to check if all evaluations are settled correctly. We cannot check amount of // contributions minted for the user, as they could have received more tokens from other participations. - pub fn assert_evaluations_settled( + pub fn assert_evaluations_migrations_created( &mut self, project_id: ProjectId, evaluations: Vec>, @@ -1403,25 +1403,17 @@ impl< ProjectsDetails::::get(project_id).unwrap().evaluation_round_info.evaluators_outcome; let account = evaluation.evaluator.clone(); assert_eq!(Evaluations::::iter_prefix_values((&project_id, &account)).count(), 0); - let reserved = ::NativeCurrency::balance_on_hold( - &(HoldReason::Evaluation(project_id).into()), - &account, - ); - assert_eq!(reserved, 0u64.into()); let (amount, should_exist) = match percentage { 0..=75 => { - assert!(::NativeCurrency::balance(&account) < evaluation.current_plmc_bond); assert!(matches!(reward_info, EvaluatorsOutcome::Slashed)); (0u64.into(), false) }, 76..=89 => { - assert!(::NativeCurrency::balance(&account) >= evaluation.current_plmc_bond); assert!(matches!(reward_info, EvaluatorsOutcome::Unchanged)); (0u64.into(), false) }, 90..=100 => { - assert!(::NativeCurrency::balance(&account) >= evaluation.current_plmc_bond); let reward = match reward_info { EvaluatorsOutcome::Rewarded(info) => Pallet::::calculate_evaluator_reward(&evaluation, &info), @@ -1444,7 +1436,7 @@ impl< } // Testing if a list of bids are settled correctly. - pub fn assert_bids_settled(&mut self, project_id: ProjectId, bids: Vec>, is_successful: bool) { + pub fn assert_bids_migrations_created(&mut self, project_id: ProjectId, bids: Vec>, is_successful: bool) { self.execute(|| { for bid in bids { let account = bid.bidder.clone(); @@ -1456,7 +1448,7 @@ impl< } // Testing if a list of contributions are settled correctly. - pub fn assert_contributions_settled( + pub fn assert_contributions_migrations_created( &mut self, project_id: ProjectId, contributions: Vec>, diff --git a/pallets/funding/src/tests.rs b/pallets/funding/src/tests.rs index 25c3b78fd..ac08d2c72 100644 --- a/pallets/funding/src/tests.rs +++ b/pallets/funding/src/tests.rs @@ -4776,9 +4776,9 @@ mod funding_end_and_settlement { inst.settle_project(project_id).unwrap(); - inst.assert_evaluations_settled(project_id, evaluations, percentage); - inst.assert_bids_settled(project_id, bids, true); - inst.assert_contributions_settled(project_id, contributions, true); + inst.assert_evaluations_migrations_created(project_id, evaluations, percentage); + inst.assert_bids_migrations_created(project_id, bids, true); + inst.assert_contributions_migrations_created(project_id, contributions, true); } #[test] @@ -4791,9 +4791,9 @@ mod funding_end_and_settlement { inst.settle_project(project_id).unwrap(); - inst.assert_evaluations_settled(project_id, evaluations, percentage); - inst.assert_bids_settled(project_id, bids, false); - inst.assert_contributions_settled(project_id, contributions, false); + inst.assert_evaluations_migrations_created(project_id, evaluations, percentage); + inst.assert_bids_migrations_created(project_id, bids, false); + inst.assert_contributions_migrations_created(project_id, contributions, false); } #[test] From bb4ec56bd2e482d619665f7d58df33bf0821bb50 Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Tue, 2 Apr 2024 14:22:17 +0200 Subject: [PATCH 20/21] fix benchmarks building --- polimec-common/common/Cargo.toml | 2 -- runtimes/politest/src/benchmarks/helpers.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/polimec-common/common/Cargo.toml b/polimec-common/common/Cargo.toml index ca3258557..542e164ad 100644 --- a/polimec-common/common/Cargo.toml +++ b/polimec-common/common/Cargo.toml @@ -49,12 +49,10 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "sp-runtime/runtime-benchmarks", - "xcm/std", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-timestamp/try-runtime", "sp-runtime/try-runtime", - "xcm/std", ] diff --git a/runtimes/politest/src/benchmarks/helpers.rs b/runtimes/politest/src/benchmarks/helpers.rs index 2c52320be..5dfee57b0 100644 --- a/runtimes/politest/src/benchmarks/helpers.rs +++ b/runtimes/politest/src/benchmarks/helpers.rs @@ -1,7 +1,7 @@ use crate::{FixedU128, Oracle, Runtime, RuntimeOrigin}; pub use frame_support::BoundedVec; pub use pallet_funding::{traits::SetPrices, AcceptedFundingAsset}; - +use scale_info::prelude::vec; pub struct SetOraclePrices; impl SetPrices for SetOraclePrices { fn set_prices() { From 134d385e725cdf762fba4350b68807a2d57d09fb Mon Sep 17 00:00:00 2001 From: Just van Stam Date: Tue, 2 Apr 2024 14:24:04 +0200 Subject: [PATCH 21/21] fmt --- pallets/funding/src/instantiator.rs | 7 ++++++- pallets/funding/src/types.rs | 7 ++----- runtimes/politest/src/lib.rs | 2 +- runtimes/shared-configuration/src/currency.rs | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs index aacb3e9ee..6cae353be 100644 --- a/pallets/funding/src/instantiator.rs +++ b/pallets/funding/src/instantiator.rs @@ -1440,7 +1440,12 @@ impl< } // Testing if a list of bids are settled correctly. - pub fn assert_bids_migrations_created(&mut self, project_id: ProjectId, bids: Vec>, is_successful: bool) { + pub fn assert_bids_migrations_created( + &mut self, + project_id: ProjectId, + bids: Vec>, + is_successful: bool, + ) { self.execute(|| { for bid in bids { let account = bid.bidder.clone(); diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index bcbd944b3..6240b5e95 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -18,16 +18,13 @@ //! Types for Funding pallet. -use crate::{ - traits::{BondingRequirementCalculation}, - BalanceOf, -}; +use crate::{traits::BondingRequirementCalculation, BalanceOf}; use frame_support::{pallet_prelude::*, traits::tokens::Balance as BalanceT}; use frame_system::pallet_prelude::BlockNumberFor; use polkadot_parachain_primitives::primitives::Id as ParaId; use serde::{Deserialize, Serialize}; use sp_arithmetic::{FixedPointNumber, FixedPointOperand}; -use sp_runtime::traits::{CheckedDiv}; +use sp_runtime::traits::CheckedDiv; use sp_std::{cmp::Eq, prelude::*}; pub use config_types::*; diff --git a/runtimes/politest/src/lib.rs b/runtimes/politest/src/lib.rs index 32c8bf3cd..675f8269e 100644 --- a/runtimes/politest/src/lib.rs +++ b/runtimes/politest/src/lib.rs @@ -37,7 +37,7 @@ use frame_support::{ }; use frame_system::{EnsureNever, EnsureRoot, EnsureRootWithSuccess, EnsureSigned}; use pallet_democracy::GetElectorate; -use pallet_funding::{DaysToBlocks}; +use pallet_funding::DaysToBlocks; use parachains_common::{ message_queue::{NarrowOriginToSibling, ParaIdToSibling}, diff --git a/runtimes/shared-configuration/src/currency.rs b/runtimes/shared-configuration/src/currency.rs index a305124df..190bbb0d7 100644 --- a/runtimes/shared-configuration/src/currency.rs +++ b/runtimes/shared-configuration/src/currency.rs @@ -16,10 +16,10 @@ use crate::Balance; use frame_support::parameter_types; -use parachains_common::AssetIdForTrustBackedAssets as AssetId; use pallet_funding::AcceptedFundingAsset; -use sp_runtime::{traits::Convert, FixedU128}; use pallet_oracle_ocw::types::AssetName; +use parachains_common::AssetIdForTrustBackedAssets as AssetId; +use sp_runtime::{traits::Convert, FixedU128}; /// One PLMC pub const PLMC: Balance = 10u128.pow(10);