diff --git a/pallets/funding/src/functions/1_application.rs b/pallets/funding/src/functions/1_application.rs index d38035fca..46470edce 100644 --- a/pallets/funding/src/functions/1_application.rs +++ b/pallets/funding/src/functions/1_application.rs @@ -40,7 +40,7 @@ impl Pallet { evaluation_round_info: EvaluationRoundInfoOf:: { total_bonded_usd: Zero::zero(), total_bonded_plmc: Zero::zero(), - evaluators_outcome: EvaluatorsOutcome::Unchanged, + evaluators_outcome: None, }, usd_bid_on_oversubscription: None, funding_end_block: None, diff --git a/pallets/funding/src/functions/3_auction.rs b/pallets/funding/src/functions/3_auction.rs index f203a6573..7ad20e26c 100644 --- a/pallets/funding/src/functions/3_auction.rs +++ b/pallets/funding/src/functions/3_auction.rs @@ -34,7 +34,7 @@ impl Pallet { project_id, project_details, ProjectStatus::AuctionInitializePeriod, - ProjectStatus::Auction, + ProjectStatus::AuctionRound, T::AuctionOpeningDuration::get(), skip_round_end_check, ) @@ -45,7 +45,6 @@ impl Pallet { #[transactional] pub fn do_end_auction(project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectMetadataNotFound)?; let bucket = Buckets::::get(project_id).ok_or(Error::::BucketNotFound)?; @@ -60,6 +59,8 @@ impl Pallet { project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size, weighted_token_price, ); + let updated_project_details = + ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; match calculation_result { Err(e) => return Err(DispatchErrorWithPostInfo { post_info: ().into(), error: e }), @@ -68,8 +69,8 @@ impl Pallet { // * Transition Round * Self::transition_project( project_id, - project_details, - ProjectStatus::Auction, + updated_project_details, + ProjectStatus::AuctionRound, ProjectStatus::CommunityRound(now.saturating_add(T::CommunityFundingDuration::get())), T::CommunityFundingDuration::get() + T::RemainderFundingDuration::get(), false, @@ -151,7 +152,7 @@ impl Pallet { ensure!(ct_amount > Zero::zero(), Error::::TooLow); ensure!(did != project_details.issuer_did, Error::::ParticipationToOwnProject); - ensure!(matches!(project_details.status, ProjectStatus::Auction), Error::::IncorrectRound); + ensure!(matches!(project_details.status, ProjectStatus::AuctionRound), Error::::IncorrectRound); ensure!( project_metadata.participation_currencies.contains(&funding_asset), Error::::FundingAssetNotAccepted diff --git a/pallets/funding/src/functions/5_funding_end.rs b/pallets/funding/src/functions/5_funding_end.rs index 7e445d9eb..5799d0afc 100644 --- a/pallets/funding/src/functions/5_funding_end.rs +++ b/pallets/funding/src/functions/5_funding_end.rs @@ -55,24 +55,17 @@ impl Pallet { // * Update Storage * DidWithActiveProjects::::set(issuer_did, None); - let evaluator_outcome = match funding_ratio { - ratio if ratio <= Perquintill::from_percent(75u64) => EvaluatorsOutcome::Slashed, - ratio if ratio < Perquintill::from_percent(90u64) => EvaluatorsOutcome::Unchanged, - _ => { - let reward_info = Self::generate_evaluator_rewards_info(project_id)?; - EvaluatorsOutcome::Rewarded(reward_info) - }, - }; - - project_details.evaluation_round_info.evaluators_outcome = evaluator_outcome; - let (next_status, duration, actual_weight) = if funding_ratio <= T::FundingSuccessThreshold::get() { + let (next_status, duration, actual_weight) = if funding_ratio < T::FundingSuccessThreshold::get() { + project_details.evaluation_round_info.evaluators_outcome = Some(EvaluatorsOutcome::Slashed); ( ProjectStatus::FundingFailed, 1u32.into(), WeightInfoOf::::end_funding_automatically_rejected_evaluators_slashed(1), ) } else { + let reward_info = Self::generate_evaluator_rewards_info(project_id)?; + project_details.evaluation_round_info.evaluators_outcome = Some(EvaluatorsOutcome::Rewarded(reward_info)); ( ProjectStatus::FundingSuccessful, T::SuccessToSettlementTime::get(), @@ -83,6 +76,8 @@ impl Pallet { let round_end = now.saturating_add(duration).saturating_sub(One::one()); project_details.round_duration.update(Some(now), Some(round_end)); project_details.status = next_status; + ProjectsDetails::::insert(project_id, project_details.clone()); + ProjectsDetails::::insert(project_id, project_details); Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) } diff --git a/pallets/funding/src/functions/6_settlement.rs b/pallets/funding/src/functions/6_settlement.rs index 794af6b0f..750d54fc1 100644 --- a/pallets/funding/src/functions/6_settlement.rs +++ b/pallets/funding/src/functions/6_settlement.rs @@ -4,9 +4,9 @@ use frame_support::{ dispatch::DispatchResult, ensure, traits::{ - fungible::MutateHold as FungibleMutateHold, - fungibles::Mutate as FungiblesMutate, - tokens::{DepositConsequence, Fortitude, Precision, Preservation, Provenance, Restriction}, + fungible::{MutateHold as FungibleMutateHold}, + fungibles::{Mutate as FungiblesMutate}, + tokens::{Fortitude, Precision, Preservation, Restriction}, Get, }, }; @@ -18,7 +18,6 @@ use sp_runtime::{ traits::{Convert, Zero}, Perquintill, }; -use frame_support::traits::fungible::Inspect; impl Pallet { #[transactional] @@ -73,7 +72,7 @@ impl Pallet { liquidity_pools_ct_amount, )?; - project_details.status = ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful); + project_details.status = ProjectStatus::SettlementStarted(FundingOutcome::Success); ProjectsDetails::::insert(project_id, &project_details); Ok(PostDispatchInfo { @@ -81,7 +80,7 @@ impl Pallet { pays_fee: Pays::Yes, }) } else { - project_details.status = ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed); + project_details.status = ProjectStatus::SettlementStarted(FundingOutcome::Failure); ProjectsDetails::::insert(project_id, &project_details); Ok(PostDispatchInfo { @@ -91,35 +90,31 @@ impl Pallet { } } - pub fn do_settle_successful_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { + pub fn do_settle_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; ensure!( - project_details.status == ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful), - Error::::FundingSuccessSettlementNotStarted + matches!(project_details.status, ProjectStatus::SettlementStarted(..)), + Error::::SettlementNotStarted ); - // 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) = + let (plmc_released, ct_rewarded): (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()), + Some(EvaluatorsOutcome::Slashed) => (Self::slash_evaluator(project_id, &evaluation)?, Zero::zero()), + Some(EvaluatorsOutcome::Rewarded(info)) => Self::reward_evaluator(project_id, &evaluation, &info)?, + None => (evaluation.current_plmc_bond, Zero::zero()), }; // Release the held PLMC bond T::NativeCurrency::release( &HoldReason::Evaluation(project_id).into(), &evaluation.evaluator, - bond, + plmc_released, Precision::Exact, )?; // Create Migration - if reward > Zero::zero() { + if ct_rewarded > Zero::zero() { let multiplier = MultiplierOf::::try_from(1u8).map_err(|_| Error::::BadMath)?; let duration = multiplier.calculate_vesting_duration::(); Self::create_migration( @@ -127,7 +122,7 @@ impl Pallet { &evaluation.evaluator, evaluation.id, ParticipationType::Evaluation, - reward, + ct_rewarded, duration, )?; } @@ -137,117 +132,70 @@ impl Pallet { project_id, account: evaluation.evaluator, id: evaluation.id, - ct_amount: reward, - slashed_plmc_amount: evaluation.current_plmc_bond.saturating_sub(bond), + plmc_released, + ct_rewarded, }); Ok(()) } - pub fn do_settle_failed_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { + pub fn do_settle_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectMetadataNotFound)?; + let funding_success = match project_details.status { + ProjectStatus::SettlementStarted(FundingOutcome::Success) => true, + _ => false, + }; ensure!( - matches!(project_details.status, ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed)), - Error::::FundingFailedSettlementNotStarted + matches!(project_details.status, ProjectStatus::SettlementStarted(..)) || bid.status == BidStatus::Rejected, + Error::::SettlementNotStarted ); - let bond = if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { - Self::slash_evaluator(project_id, &evaluation)? - } else { - evaluation.current_plmc_bond - }; + // Return either the full amount to refund if bid is rejected/project failed, or a partial amount when the wap > paid price + let (refunded_plmc, refunded_funding_asset_amount) = Self::calculate_refund(&bid, funding_success)?; - // Release the held PLMC bond - T::NativeCurrency::release( - &HoldReason::Evaluation(project_id).into(), - &evaluation.evaluator, - bond, - Precision::Exact, - )?; + Self::release_participation_bond(project_id, &bid.bidder, refunded_plmc)?; + Self::release_funding_asset(project_id, &bid.bidder, refunded_funding_asset_amount, bid.funding_asset)?; - Evaluations::::remove((project_id, evaluation.evaluator.clone(), evaluation.id)); + if funding_success && bid.status != BidStatus::Rejected { + let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; - Self::deposit_event(Event::EvaluationSettled { - project_id, - account: evaluation.evaluator, - id: evaluation.id, - ct_amount: Zero::zero(), - slashed_plmc_amount: evaluation.current_plmc_bond.saturating_sub(bond), - }); - - Ok(()) - } - - pub fn do_settle_successful_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectMetadataNotFound)?; - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let plmc_vesting_info = + Self::calculate_vesting_info(&bid.bidder, bid.multiplier, bid.plmc_bond.saturating_sub(refunded_plmc)) + .map_err(|_| Error::::BadMath)?; - ensure!( - project_details.status == ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful), - Error::::FundingSuccessSettlementNotStarted - ); - ensure!( - matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), - Error::::ImpossibleState - ); - ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::TooEarlyForRound); - - let (refund_plmc, refund_funding_asset) = Self::calculate_refund(&bid)?; - - 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 new_bond = bid.plmc_bond.saturating_sub(refund_plmc); - let vest_info = - Self::calculate_vesting_info(&bidder, bid.multiplier, new_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 { - if refund_plmc > Zero::zero() { - Self::release_participation_bond(project_id, &bidder, refund_plmc)?; - } T::Vesting::add_release_schedule( - &bidder, - vest_info.total_amount, - vest_info.amount_per_block, + &bid.bidder, + plmc_vesting_info.total_amount, + plmc_vesting_info.amount_per_block, funding_end_block, HoldReason::Participation(project_id).into(), )?; - } else { - // Release the held PLMC bond - Self::release_participation_bond(project_id, &bidder, bid.plmc_bond)?; - } - - // Mint the contribution tokens - Self::mint_contribution_tokens(project_id, &bidder, bid.final_ct_amount)?; - let new_funding_asset_amount_locked = bid.funding_asset_amount_locked.saturating_sub(refund_funding_asset); - if refund_funding_asset > Zero::zero() { - Self::release_funding_asset(project_id, &bidder, refund_funding_asset, bid.funding_asset)?; - } + Self::mint_contribution_tokens(project_id, &bid.bidder, bid.final_ct_amount)?; - // Payout the bid funding asset amount to the project account - Self::release_funding_asset( - project_id, - &project_metadata.funding_destination_account, - new_funding_asset_amount_locked, - bid.funding_asset, - )?; + Self::create_migration( + project_id, + &bid.bidder, + bid.id, + ParticipationType::Bid, + bid.final_ct_amount, + plmc_vesting_info.duration, + )?; - Self::create_migration( - project_id, - &bidder, - bid.id, - ParticipationType::Bid, - bid.final_ct_amount, - vest_info.duration, - )?; + Self::release_funding_asset( + project_id, + &project_metadata.funding_destination_account, + bid.funding_asset_amount_locked.saturating_sub(refunded_funding_asset_amount), + bid.funding_asset, + )?; + } - Bids::::remove((project_id, bidder.clone(), bid.id)); + Bids::::remove((project_id, bid.bidder.clone(), bid.id)); Self::deposit_event(Event::BidSettled { project_id, - account: bidder, + account: bid.bidder, id: bid.id, ct_amount: bid.final_ct_amount, }); @@ -257,151 +205,89 @@ impl Pallet { /// Calculate the amount of funds the biider should receive back based on the original bid /// amount and price compared to the final bid amount and price. - fn calculate_refund(bid: &BidInfoOf) -> Result<(BalanceOf, BalanceOf), DispatchError> { - let new_ticket_size = bid.final_ct_usd_price.checked_mul_int(bid.final_ct_amount).ok_or(Error::::BadMath)?; + fn calculate_refund( + bid: &BidInfoOf, + funding_success: bool, + ) -> Result<(BalanceOf, BalanceOf), DispatchError> { + if bid.status == BidStatus::Rejected || !funding_success { + return Ok((bid.plmc_bond, bid.funding_asset_amount_locked)); + } + let new_ticket_size = bid.final_ct_usd_price.checked_mul_int(bid.final_ct_amount).ok_or(Error::::BadMath)?; let new_plmc_bond = Self::calculate_plmc_bond(new_ticket_size, bid.multiplier)?; let new_funding_asset_amount = Self::calculate_funding_asset_amount(new_ticket_size, bid.funding_asset)?; - let mut refund_plmc = bid.plmc_bond.saturating_sub(new_plmc_bond); - let mut refund_funding_asset = bid.funding_asset_amount_locked.saturating_sub(new_funding_asset_amount); - if T::FundingCurrency::can_deposit( - bid.funding_asset.to_assethub_id(), - &bid.bidder, - refund_funding_asset, - Provenance::Extant, - ) != DepositConsequence::Success - { - refund_funding_asset = Zero::zero(); - } - if T::NativeCurrency::can_deposit(&bid.bidder, refund_plmc, Provenance::Extant) != DepositConsequence::Success { - refund_plmc = Zero::zero(); - } + let refund_plmc = bid.plmc_bond.saturating_sub(new_plmc_bond); + let refund_funding_asset = bid.funding_asset_amount_locked.saturating_sub(new_funding_asset_amount); Ok((refund_plmc, refund_funding_asset)) } - 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::SettlementStarted(FundingOutcome::FundingFailed)) || - bid.status == BidStatus::Rejected, - Error::::FundingFailedSettlementNotStarted - ); - - 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_participation_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 { + pub fn do_settle_contribution(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectMetadataNotFound)?; 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::SettlementStarted(FundingOutcome::FundingSuccessful), - Error::::FundingSuccessSettlementNotStarted - ); - ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::TooEarlyForRound); + let funding_end_block = project_details.funding_end_block.ok_or(Error::::ImpossibleState)?; + let mut final_ct_amount = Zero::zero(); - let contributor = contribution.contributor; + let ProjectStatus::SettlementStarted(outcome) = project_details.status else { + return Err(Error::::SettlementNotStarted.into()); + }; + if outcome == FundingOutcome::Failure { + // Release the held PLMC bond + Self::release_participation_bond(project_id, &contribution.contributor, contribution.plmc_bond)?; - // 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) + Self::release_funding_asset( + project_id, + &contribution.contributor, + contribution.funding_asset_amount, + contribution.funding_asset, + )?; + } else { + // Calculate the vesting info and add the release schedule + let vest_info = Self::calculate_vesting_info( + &contribution.contributor, + contribution.multiplier, + contribution.plmc_bond, + ) .map_err(|_| Error::::BadMath)?; - if contribution.multiplier.into() > 1u8 { T::Vesting::add_release_schedule( - &contributor, + &contribution.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_participation_bond(project_id, &contributor, contribution.plmc_bond)?; - } - - // Mint the contribution tokens - Self::mint_contribution_tokens(project_id, &contributor, contribution.ct_amount)?; - - // Payout the bid funding asset amount to the project account - Self::release_funding_asset( - project_id, - &project_metadata.funding_destination_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)?; + // Mint the contribution tokens + Self::mint_contribution_tokens(project_id, &contribution.contributor, contribution.ct_amount)?; - ensure!( - matches!(project_details.status, ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed)), - Error::::FundingFailedSettlementNotStarted - ); - - // Check if the bidder has a future deposit held - let contributor = contribution.contributor; + // Payout the bid funding asset amount to the project account + Self::release_funding_asset( + project_id, + &project_metadata.funding_destination_account, + contribution.funding_asset_amount, + contribution.funding_asset, + )?; - // Return the funding assets to the contributor - Self::release_funding_asset( - project_id, - &contributor, - contribution.funding_asset_amount, - contribution.funding_asset, - )?; + // Create Migration + Self::create_migration( + project_id, + &contribution.contributor, + contribution.id, + ParticipationType::Contribution, + contribution.ct_amount, + vest_info.duration, + )?; - // Release the held PLMC bond - Self::release_participation_bond(project_id, &contributor, contribution.plmc_bond)?; + final_ct_amount = contribution.ct_amount; + } - // Remove the bid from the storage - Contributions::::remove((project_id, contributor.clone(), contribution.id)); + Contributions::::remove((project_id, contribution.contributor.clone(), contribution.id)); Self::deposit_event(Event::ContributionSettled { project_id, - account: contributor, + account: contribution.contributor, id: contribution.id, - ct_amount: Zero::zero(), + ct_amount: final_ct_amount, }); Ok(()) @@ -450,6 +336,9 @@ impl Pallet { amount: BalanceOf, asset: AcceptedFundingAsset, ) -> DispatchResult { + if amount.is_zero() { + return Ok(()); + } let project_pot = Self::fund_account_id(project_id); T::FundingCurrency::transfer( asset.to_assethub_id(), @@ -466,6 +355,9 @@ impl Pallet { participant: &AccountIdOf, amount: BalanceOf, ) -> DispatchResult { + if amount.is_zero() { + return Ok(()); + } // Release the held PLMC bond T::NativeCurrency::release( &HoldReason::Participation(project_id).into(), diff --git a/pallets/funding/src/functions/7_ct_migration.rs b/pallets/funding/src/functions/7_ct_migration.rs index c19b8bfa7..b09ec7e78 100644 --- a/pallets/funding/src/functions/7_ct_migration.rs +++ b/pallets/funding/src/functions/7_ct_migration.rs @@ -9,8 +9,8 @@ impl Pallet { ensure!(project_details.issuer_account == caller, Error::::NotIssuer); match project_details.status { - ProjectStatus::SettlementFinished(FundingOutcome::FundingSuccessful) => (), - ProjectStatus::FundingSuccessful | ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful) => + ProjectStatus::SettlementFinished(FundingOutcome::Success) => (), + ProjectStatus::FundingSuccessful | ProjectStatus::SettlementStarted(FundingOutcome::Success) => return Err(Error::::SettlementNotComplete.into()), _ => return Err(Error::::IncorrectRound.into()), } @@ -56,8 +56,8 @@ impl Pallet { // * Validity checks * ensure!(&(project_details.issuer_account) == caller, Error::::NotIssuer); match project_details.status { - ProjectStatus::SettlementFinished(FundingOutcome::FundingSuccessful) => (), - ProjectStatus::FundingSuccessful | ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful) => + ProjectStatus::SettlementFinished(FundingOutcome::Success) => (), + ProjectStatus::FundingSuccessful | ProjectStatus::SettlementStarted(FundingOutcome::Success) => return Err(Error::::SettlementNotComplete.into()), _ => return Err(Error::::IncorrectRound.into()), } diff --git a/pallets/funding/src/functions/misc.rs b/pallets/funding/src/functions/misc.rs index fb2138c5e..64ec1a954 100644 --- a/pallets/funding/src/functions/misc.rs +++ b/pallets/funding/src/functions/misc.rs @@ -79,7 +79,6 @@ impl Pallet { auction_allocation_size: BalanceOf, wap: PriceOf, ) -> Result<(u32, u32), DispatchError> { - // Get all the bids that were made before the end of the closing period. let mut bids = Bids::::iter_prefix_values((project_id,)).collect::>(); // temp variable to store the sum of the bids let mut bid_token_amount_sum = Zero::zero(); @@ -91,6 +90,7 @@ impl Pallet { let buyable_amount = auction_allocation_size.saturating_sub(bid_token_amount_sum); if buyable_amount.is_zero() { bid.status = BidStatus::Rejected; + bid.final_ct_amount = Zero::zero(); } else if bid.original_ct_amount <= buyable_amount { if bid.final_ct_usd_price > wap { bid.final_ct_usd_price = wap; @@ -126,6 +126,8 @@ impl Pallet { if let Some(info) = maybe_info { info.remaining_contribution_tokens.saturating_reduce(bid_token_amount_sum); info.funding_amount_reached_usd.saturating_accrue(total_auction_allocation_usd); + info.weighted_average_price = Some(wap); + Ok(()) } else { Err(Error::::ProjectDetailsNotFound.into()) diff --git a/pallets/funding/src/instantiator/chain_interactions.rs b/pallets/funding/src/instantiator/chain_interactions.rs index 8fb2e8437..7a1274280 100644 --- a/pallets/funding/src/instantiator/chain_interactions.rs +++ b/pallets/funding/src/instantiator/chain_interactions.rs @@ -310,7 +310,7 @@ impl< evaluation_round_info: EvaluationRoundInfoOf:: { total_bonded_usd: Zero::zero(), total_bonded_plmc: Zero::zero(), - evaluators_outcome: EvaluatorsOutcome::Unchanged, + evaluators_outcome: None, }, usd_bid_on_oversubscription: None, funding_end_block: None, @@ -464,7 +464,7 @@ impl< self.execute(|| crate::Pallet::::do_start_auction(caller, project_id).unwrap()); - assert_eq!(self.get_project_details(project_id).status, ProjectStatus::Auction); + assert_eq!(self.get_project_details(project_id).status, ProjectStatus::AuctionRound); Ok(()) } @@ -695,49 +695,19 @@ 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::SettlementStarted(FundingOutcome::FundingSuccessful) => - self.settle_successful_project(project_id).unwrap(), - ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) => - self.settle_failed_project(project_id).unwrap(), - _ => panic!("Project should be in SettlementStarted status"), - } - self.execute(|| { - crate::Pallet::::do_mark_project_as_settled(project_id).unwrap(); - }); - Ok(()) - } - - fn settle_successful_project(&mut self, project_id: ProjectId) -> Result<(), DispatchError> { - self.execute(|| { - 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))?; - - Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { - Pallet::::do_settle_successful_contribution(contribution, project_id) - }) - }) - } - - fn settle_failed_project(&mut self, project_id: ProjectId) -> Result<(), DispatchError> { + pub fn settle_project(&mut self, project_id: ProjectId) { self.execute(|| { Evaluations::::iter_prefix((project_id,)) - .try_for_each(|(_, evaluation)| Pallet::::do_settle_failed_evaluation(evaluation, project_id))?; + .for_each(|(_, evaluation)| Pallet::::do_settle_evaluation(evaluation, project_id).unwrap()); Bids::::iter_prefix((project_id,)) - .try_for_each(|(_, bid)| Pallet::::do_settle_failed_bid(bid, project_id))?; + .for_each(|(_, bid)| Pallet::::do_settle_bid(bid, project_id).unwrap()); - Contributions::::iter_prefix((project_id,)).try_for_each(|(_, contribution)| { - Pallet::::do_settle_failed_contribution(contribution, project_id) - })?; + Contributions::::iter_prefix((project_id,)) + .for_each(|(_, contribution)| Pallet::::do_settle_contribution(contribution, project_id).unwrap()); - Ok(()) - }) + crate::Pallet::::do_mark_project_as_settled(project_id).unwrap(); + }); } pub fn get_evaluations(&mut self, project_id: ProjectId) -> Vec> { @@ -807,35 +777,23 @@ impl< &mut self, project_id: ProjectId, evaluations: Vec>, - percentage: u64, ) { let details = self.get_project_details(project_id); assert!(matches!(details.status, ProjectStatus::SettlementFinished(_))); for evaluation in evaluations { - let reward_info = self - .execute(|| ProjectsDetails::::get(project_id).unwrap().evaluation_round_info.evaluators_outcome); + let reward_info = self.execute(|| { + ProjectsDetails::::get(project_id).unwrap().evaluation_round_info.evaluators_outcome.unwrap() + }); let account = evaluation.evaluator.clone(); assert_eq!(self.execute(|| { Evaluations::::iter_prefix_values((&project_id, &account)).count() }), 0); - let (amount, should_exist) = match percentage { - 0..=75 => { - assert!(matches!(reward_info, EvaluatorsOutcome::Slashed)); - (0u64.into(), false) - }, - 76..=89 => { - assert!(matches!(reward_info, EvaluatorsOutcome::Unchanged)); - (0u64.into(), false) - }, - 90..=100 => { - let reward = match reward_info { - EvaluatorsOutcome::Rewarded(info) => - Pallet::::calculate_evaluator_reward(&evaluation, &info), - _ => panic!("Evaluators should be rewarded"), - }; - (reward, true) - }, - _ => panic!("Percentage should be between 0 and 100"), + let (amount, should_exist) = { + let reward = match reward_info { + EvaluatorsOutcome::Rewarded(info) => Pallet::::calculate_evaluator_reward(&evaluation, &info), + _ => panic!("Evaluators should be rewarded"), + }; + (reward, true) }; self.assert_migration( project_id, @@ -1130,7 +1088,7 @@ impl< let settlement_start = self.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); self.jump_to_block(settlement_start); - self.settle_project(project_id).unwrap(); + self.settle_project(project_id); project_id } @@ -1156,7 +1114,7 @@ impl< ), ProjectStatus::CommunityRound(..) => self.create_community_contributing_project(project_metadata, issuer, None, evaluations, bids), - ProjectStatus::Auction => self.create_auctioning_project(project_metadata, issuer, None, evaluations), + ProjectStatus::AuctionRound => self.create_auctioning_project(project_metadata, issuer, None, evaluations), ProjectStatus::EvaluationRound => self.create_evaluating_project(project_metadata, issuer, None), ProjectStatus::Application => self.create_new_project(project_metadata, issuer, None), _ => panic!("unsupported project creation in that status"), diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index a6433466e..3d40779dd 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -170,6 +170,7 @@ pub type AssetIdOf = <::FundingCurrency as fungibles::Inspect<::AccountId>>::AssetId; pub type RewardInfoOf = RewardInfo>; pub type EvaluatorsOutcomeOf = EvaluatorsOutcome>; +pub type VestingInfoOf = VestingInfo, BalanceOf>; pub type TicketSizeOf = TicketSize>; pub type ProjectMetadataOf = @@ -621,8 +622,8 @@ pub mod pallet { project_id: ProjectId, account: AccountIdOf, id: u32, - ct_amount: BalanceOf, - slashed_plmc_amount: BalanceOf, + ct_rewarded: BalanceOf, + plmc_released: BalanceOf, }, BidSettled { project_id: ProjectId, @@ -795,10 +796,8 @@ pub mod pallet { WrongParaId, /// Migration channel is not ready for migrations. ChannelNotReady, - /// Settlement for this project/outcome has not yet started. - FundingSuccessSettlementNotStarted, - /// Settlement for this project/outcome has not yet started. - FundingFailedSettlementNotStarted, + /// Settlement for this project has not yet started. + SettlementNotStarted, /// Wanted to settle as successful when it failed, or vice versa. WrongSettlementOutcome, /// User still has participations that need to be settled before migration. @@ -890,10 +889,8 @@ pub mod pallet { /// Any bids from this point until the auction_closing starts, will be considered as valid. #[pallet::call_index(6)] #[pallet::weight(WeightInfoOf::::start_auction_manually(1))] - pub fn start_auction(origin: OriginFor, jwt: UntrustedToken, project_id: ProjectId) -> DispatchResult { - let (account, _did, investor_type, _cid) = - T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); + pub fn start_auction(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let account = ensure_signed(origin)?; Self::do_start_auction(account, project_id) } @@ -984,7 +981,7 @@ pub mod pallet { #[pallet::call_index(12)] #[pallet::weight(WeightInfoOf::::settle_successful_evaluation())] - pub fn settle_successful_evaluation( + pub fn settle_evaluation( origin: OriginFor, project_id: ProjectId, evaluator: AccountIdOf, @@ -993,53 +990,12 @@ pub mod pallet { let _caller = ensure_signed(origin)?; let bid = Evaluations::::get((project_id, evaluator, evaluation_id)) .ok_or(Error::::ParticipationNotFound)?; - Self::do_settle_successful_evaluation(bid, project_id) + Self::do_settle_evaluation(bid, project_id) } #[pallet::call_index(13)] #[pallet::weight(WeightInfoOf::::settle_successful_bid())] - pub fn settle_successful_bid( - origin: OriginFor, - project_id: ProjectId, - bidder: AccountIdOf, - bid_id: u32, - ) -> DispatchResult { - let _caller = ensure_signed(origin)?; - let bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::ParticipationNotFound)?; - Self::do_settle_successful_bid(bid, project_id) - } - - #[pallet::call_index(14)] - #[pallet::weight(WeightInfoOf::::settle_successful_contribution())] - pub fn settle_successful_contribution( - origin: OriginFor, - project_id: ProjectId, - contributor: AccountIdOf, - contribution_id: u32, - ) -> DispatchResult { - let _caller = ensure_signed(origin)?; - let bid = Contributions::::get((project_id, contributor, contribution_id)) - .ok_or(Error::::ParticipationNotFound)?; - Self::do_settle_successful_contribution(bid, project_id) - } - - #[pallet::call_index(15)] - #[pallet::weight(WeightInfoOf::::settle_failed_evaluation())] - pub fn settle_failed_evaluation( - origin: OriginFor, - project_id: ProjectId, - evaluator: AccountIdOf, - evaluation_id: u32, - ) -> DispatchResult { - let _caller = ensure_signed(origin)?; - let bid = Evaluations::::get((project_id, evaluator, evaluation_id)) - .ok_or(Error::::ParticipationNotFound)?; - Self::do_settle_failed_evaluation(bid, project_id) - } - - #[pallet::call_index(16)] - #[pallet::weight(WeightInfoOf::::settle_failed_bid())] - pub fn settle_failed_bid( + pub fn settle_bid( origin: OriginFor, project_id: ProjectId, bidder: AccountIdOf, @@ -1047,12 +1003,12 @@ pub mod pallet { ) -> DispatchResult { let _caller = ensure_signed(origin)?; let bid = Bids::::get((project_id, bidder, bid_id)).ok_or(Error::::ParticipationNotFound)?; - Self::do_settle_failed_bid(bid, project_id) + Self::do_settle_bid(bid, project_id) } #[pallet::call_index(17)] #[pallet::weight(WeightInfoOf::::settle_failed_contribution())] - pub fn settle_failed_contribution( + pub fn settle_contribution( origin: OriginFor, project_id: ProjectId, contributor: AccountIdOf, @@ -1061,7 +1017,7 @@ pub mod pallet { let _caller = ensure_signed(origin)?; let bid = Contributions::::get((project_id, contributor, contribution_id)) .ok_or(Error::::ParticipationNotFound)?; - Self::do_settle_failed_contribution(bid, project_id) + Self::do_settle_contribution(bid, project_id) } #[pallet::call_index(18)] diff --git a/pallets/funding/src/tests/1_application.rs b/pallets/funding/src/tests/1_application.rs index 2f39a2f83..4a7423b10 100644 --- a/pallets/funding/src/tests/1_application.rs +++ b/pallets/funding/src/tests/1_application.rs @@ -1,1269 +1,1269 @@ -use super::*; -use polimec_common::credentials::InvestorType; -use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt_with_cid}; - -#[cfg(test)] -mod round_flow { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn application_round_completed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - - inst.create_evaluating_project(project_metadata, issuer, None); - } - } -} - -#[cfg(test)] -mod create_project_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use polimec_common_test_utils::get_mock_jwt_with_cid; - - #[test] - fn project_id_autoincrement_works() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_1 = default_project_metadata(ISSUER_1); - let project_2 = default_project_metadata(ISSUER_2); - let project_3 = default_project_metadata(ISSUER_3); - - let created_project_1_id = inst.create_evaluating_project(project_1, ISSUER_1, None); - let created_project_2_id = inst.create_evaluating_project(project_2, ISSUER_2, None); - let created_project_3_id = inst.create_evaluating_project(project_3, ISSUER_3, None); - - assert_eq!(created_project_1_id, 0); - assert_eq!(created_project_2_id, 1); - assert_eq!(created_project_3_id, 2); - } - - #[test] - fn multiple_creations_different_issuers() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut issuer = ISSUER_1; - for _ in 0..512 { - let project_metadata = default_project_metadata(issuer); - inst.create_evaluating_project(project_metadata, issuer, None); - inst.advance_time(1u64).unwrap(); - issuer += 1; - } - } - - #[test] - fn multiple_funding_currencies() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let default_project_metadata = default_project_metadata(ISSUER_1); - - let mut one_currency_1 = default_project_metadata.clone(); - one_currency_1.participation_currencies = vec![AcceptedFundingAsset::USDT].try_into().unwrap(); - - let mut one_currency_2 = default_project_metadata.clone(); - one_currency_2.participation_currencies = vec![AcceptedFundingAsset::USDC].try_into().unwrap(); - - let mut one_currency_3 = default_project_metadata.clone(); - one_currency_3.participation_currencies = vec![AcceptedFundingAsset::DOT].try_into().unwrap(); - - let mut two_currencies_1 = default_project_metadata.clone(); - two_currencies_1.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC].try_into().unwrap(); - - let mut two_currencies_2 = default_project_metadata.clone(); - two_currencies_2.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::DOT].try_into().unwrap(); - - let mut two_currencies_3 = default_project_metadata.clone(); - two_currencies_3.participation_currencies = - vec![AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT].try_into().unwrap(); - - let mut three_currencies = default_project_metadata.clone(); - three_currencies.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] - .try_into() - .unwrap(); - - let projects = vec![ - one_currency_1.clone(), - one_currency_2.clone(), - one_currency_3, - two_currencies_1, - two_currencies_2, - two_currencies_3, - three_currencies, - ]; - - let mut issuer = ISSUER_1; - for project in projects { - issuer += 1; - let issuer_mint = (issuer, 1000 * PLMC).into(); - inst.mint_plmc_to(vec![issuer_mint]); - assert_ok!(inst.execute(|| { - Pallet::::do_create_project(&issuer, project, generate_did_from_account(issuer)) - })); - } - } - - #[test] - fn issuer_can_create_second_project_after_first_is_inactive() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer: AccountId = ISSUER_1; - let did: Did = BoundedVec::new(); - let project_metadata: ProjectMetadataOf = default_project_metadata(issuer); - let jwt: UntrustedToken = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - did, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - - let failing_bids = vec![(BIDDER_1, 1000 * CT_UNIT).into(), (BIDDER_2, 1000 * CT_UNIT).into()]; - - inst.mint_plmc_to(default_plmc_balances()); - inst.mint_foreign_asset_to(default_usdt_balances()); - - // Cannot create 2 projects consecutively - inst.execute(|| { - assert_ok!(Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - ), - Error::::HasActiveProject - ); - }); - - // A Project is "inactive" after the evaluation fails - inst.start_evaluation(0, ISSUER_1).unwrap(); - inst.execute(|| { - assert_noop!( - Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - ), - Error::::HasActiveProject - ); - }); - inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); - assert_eq!( - inst.get_project_details(0).status, - ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) - ); - inst.execute(|| { - assert_ok!(Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - )); - }); - - // A Project is "inactive" after the funding fails - inst.start_evaluation(1, ISSUER_1).unwrap(); - inst.evaluate_for_users(1, default_evaluations()).unwrap(); - inst.start_auction(1, ISSUER_1).unwrap(); - inst.bid_for_users(1, failing_bids).unwrap(); - inst.start_community_funding(1).unwrap(); - inst.execute(|| { - assert_noop!( - Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - ), - Error::::HasActiveProject - ); - }); - inst.finish_funding(1, None).unwrap(); - assert_eq!(inst.get_project_details(1).status, ProjectStatus::FundingFailed); - inst.execute(|| { - assert_ok!(Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - )); - }); - - // A project is "inactive" after the funding succeeds - inst.start_evaluation(2, ISSUER_1).unwrap(); - inst.evaluate_for_users(2, default_evaluations()).unwrap(); - inst.start_auction(2, ISSUER_1).unwrap(); - inst.bid_for_users(2, default_bids()).unwrap(); - inst.start_community_funding(2).unwrap(); - inst.contribute_for_users(2, default_community_buys()).unwrap(); - inst.start_remainder_or_end_funding(2).unwrap(); - inst.contribute_for_users(2, default_remainder_buys()).unwrap(); - inst.finish_funding(2, None).unwrap(); - assert_eq!(inst.get_project_details(2).status, ProjectStatus::FundingSuccessful); - assert_ok!(inst.execute(|| crate::Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - ))); - } - - #[test] - fn shitcoin_tokenomics() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - - // funding target of 1000 USD at 100 trillion supply - const QUADRILLION_SUPPLY: u128 = 100_000_000_000_000 * CT_UNIT; - // at the lowest possible price, which makes a funding target of 1 bn USD - const LOW_PRICE: f64 = 0.00001f64; - - project_metadata.mainnet_token_max_supply = QUADRILLION_SUPPLY; - project_metadata.total_allocation_size = QUADRILLION_SUPPLY; - project_metadata.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( - FixedU128::from_float(LOW_PRICE), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(); - - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_ok!(crate::Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt, - project_metadata - )); - }); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn non_institutional_credential_fails() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Retail, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - crate::Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt, - project_metadata.clone() - ), - Error::::WrongInvestorType - ); - }); - - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Professional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - crate::Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt, - project_metadata - ), - Error::::WrongInvestorType - ); - }); - } - - #[test] - fn did_cannot_have_2_active_projects() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let ed = inst.get_ed(); - let issuer_mint: UserToPLMCBalance = (ISSUER_1, ed * 2).into(); - // Create a first project - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.mint_plmc_to(vec![issuer_mint.clone()]); - inst.execute(|| { - assert_ok!(Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - )); - }); - - // different account, same did - let jwt = get_mock_jwt_with_cid( - ISSUER_2, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_2), - jwt.clone(), - project_metadata.clone() - ), - Error::::HasActiveProject - ); - }); - } - - #[test] - fn not_enough_plmc_for_escrow_ed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let ed = inst.get_ed(); - inst.mint_plmc_to(vec![UserToPLMCBalance::new(ISSUER_1, ed)]); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata,), - Error::::IssuerNotEnoughFunds - ); - }); - } - - // Invalid metadata tests: - #[test] - fn mainnet_supply_less_than_allocation() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.total_allocation_size = 100_000_001 * CT_UNIT; - project_metadata.mainnet_token_max_supply = 100_000_000 * CT_UNIT; - inst.mint_plmc_to(default_plmc_balances()); - inst.execute(|| { - assert_noop!( - Pallet::::do_create_project( - &ISSUER_1, - project_metadata, - generate_did_from_account(ISSUER_1), - ), - Error::::AllocationSizeError - ); - }); - } - - #[test] - fn invalid_ticket_sizes() { - let correct_project = default_project_metadata(ISSUER_1); - - // min in bidding below 5k - let mut wrong_project_1 = correct_project.clone(); - wrong_project_1.bidding_ticket_sizes.professional = TicketSize::new(4999 * USD_UNIT, None); - - let mut wrong_project_2 = correct_project.clone(); - wrong_project_2.bidding_ticket_sizes.institutional = TicketSize::new(4999 * USD_UNIT, None); - - let mut wrong_project_3 = correct_project.clone(); - wrong_project_3.bidding_ticket_sizes.professional = TicketSize::new(3000 * USD_UNIT, None); - wrong_project_3.bidding_ticket_sizes.institutional = TicketSize::new(0 * USD_UNIT, None); - - let mut wrong_project_4 = correct_project.clone(); - wrong_project_4.bidding_ticket_sizes.professional = TicketSize::new(USD_UNIT, None); - wrong_project_4.bidding_ticket_sizes.institutional = TicketSize::new(USD_UNIT, None); - - // min in contributing below 1 USD - let mut wrong_project_5 = correct_project.clone(); - wrong_project_5.contributing_ticket_sizes.retail = TicketSize::new(USD_UNIT / 2, None); - - let mut wrong_project_6 = correct_project.clone(); - wrong_project_6.contributing_ticket_sizes.professional = TicketSize::new(USD_UNIT / 2, None); - - let mut wrong_project_7 = correct_project.clone(); - wrong_project_7.contributing_ticket_sizes.institutional = TicketSize::new(USD_UNIT / 2, None); - - // min higher than max - let mut wrong_project_8 = correct_project.clone(); - wrong_project_8.bidding_ticket_sizes.professional = TicketSize::new(5000 * USD_UNIT, Some(4990 * USD_UNIT)); - - let mut wrong_project_9 = correct_project.clone(); - wrong_project_9.bidding_ticket_sizes.institutional = - TicketSize::new(6000 * USD_UNIT, Some(5500 * USD_UNIT)); - - let mut wrong_project_10 = correct_project.clone(); - wrong_project_10.contributing_ticket_sizes.retail = TicketSize::new(6000 * USD_UNIT, Some(5500 * USD_UNIT)); - - let mut wrong_project_11 = correct_project.clone(); - wrong_project_11.contributing_ticket_sizes.professional = - TicketSize::new(6000 * USD_UNIT, Some(5500 * USD_UNIT)); - - let mut wrong_project_12 = correct_project.clone(); - wrong_project_12.contributing_ticket_sizes.professional = - TicketSize::new(6000 * USD_UNIT, Some(5500 * USD_UNIT)); - - let wrong_projects = vec![ - wrong_project_1.clone(), - wrong_project_2, - wrong_project_3.clone(), - wrong_project_4, - wrong_project_5, - wrong_project_6, - wrong_project_7, - wrong_project_8, - wrong_project_9, - wrong_project_10, - wrong_project_11, - wrong_project_12, - ]; - - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - inst.mint_plmc_to(default_plmc_balances()); - - for project in wrong_projects { - let project_err = inst.execute(|| { - Pallet::::do_create_project(&ISSUER_1, project, generate_did_from_account(ISSUER_1)) - .unwrap_err() - }); - assert_eq!(project_err, Error::::TicketSizeError.into()); - } - } - - #[test] - fn duplicated_participation_currencies() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut issuer = ISSUER_1; - let default_project_metadata = default_project_metadata(ISSUER_1); - - let mut wrong_project_1 = default_project_metadata.clone(); - wrong_project_1.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDT].try_into().unwrap(); - - let mut wrong_project_2 = default_project_metadata.clone(); - wrong_project_2.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDT] - .try_into() - .unwrap(); - - let mut wrong_project_3 = default_project_metadata.clone(); - wrong_project_3.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::USDT] - .try_into() - .unwrap(); - - let mut wrong_project_4 = default_project_metadata.clone(); - wrong_project_4.participation_currencies = - vec![AcceptedFundingAsset::DOT, AcceptedFundingAsset::DOT, AcceptedFundingAsset::USDC] - .try_into() - .unwrap(); - - let wrong_projects = vec![wrong_project_1, wrong_project_2, wrong_project_3, wrong_project_4]; - for project in wrong_projects { - issuer += 1; - let issuer_mint = (issuer, 1000 * PLMC).into(); - inst.mint_plmc_to(vec![issuer_mint]); - let project_err = inst.execute(|| { - Pallet::::do_create_project(&issuer, project, generate_did_from_account(issuer)) - .unwrap_err() - }); - assert_eq!(project_err, Error::::ParticipationCurrenciesError.into()); - } - } - - #[test] - fn price_zero() { - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.minimum_price = 0_u128.into(); - - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - inst.mint_plmc_to(default_plmc_balances()); - let project_err = inst.execute(|| { - Pallet::::do_create_project( - &ISSUER_1, - project_metadata, - generate_did_from_account(ISSUER_1), - ) - .unwrap_err() - }); - assert_eq!(project_err, Error::::PriceTooLow.into()); - } - - #[test] - fn allocation_zero() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.total_allocation_size = 0; - - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata), - Error::::AllocationSizeError - ); - }); - } - - #[test] - fn auction_round_percentage_zero() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.auction_round_allocation_percentage = Percent::from_percent(0); - - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata), - Error::::AuctionRoundPercentageError - ); - }); - } - - #[test] - fn target_funding_less_than_1000_usd() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.minimum_price = >::calculate_decimals_aware_price( - PriceOf::::from_float(1.0), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(); - project_metadata.total_allocation_size = 999 * CT_UNIT; - project_metadata.mainnet_token_max_supply = 999 * CT_UNIT; - - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - ), - Error::::FundingTargetTooLow - ); - }); - - project_metadata.minimum_price = >::calculate_decimals_aware_price( - PriceOf::::from_float(0.0001), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(); - project_metadata.total_allocation_size = 9999999u128 * CT_UNIT; - project_metadata.mainnet_token_max_supply = 9999999u128 * CT_UNIT; - inst.execute(|| { - assert_noop!( - Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata), - Error::::FundingTargetTooLow - ); - }); - } - - #[test] - fn unaccepted_decimal_ranges() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let mut fail_with_decimals = |decimals: u8| { - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.token_information.decimals = decimals; - project_metadata.total_allocation_size = 100_000 * 10u128.pow(decimals.into()); - project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; - project_metadata.minimum_price = - ::PriceProvider::calculate_decimals_aware_price( - PriceOf::::from_float(10_000.0f64), - USD_DECIMALS, - project_metadata.token_information.decimals, - ) - .unwrap(); - - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - ), - Error::::BadDecimals - ); - }); - }; - - // less than 6 should fail - for i in 0..=5 { - fail_with_decimals(i); - } - - // more than 18 should fail - for i in 19..=24 { - fail_with_decimals(i); - } - - let mut issuer = ISSUER_2; - let mut succeed_with_decimals = |decimals: u8| { - let mut project_metadata = default_project_metadata(issuer); - project_metadata.token_information.decimals = decimals; - project_metadata.total_allocation_size = 100_000 * 10u128.pow(decimals.into()); - project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; - project_metadata.minimum_price = - ::PriceProvider::calculate_decimals_aware_price( - PriceOf::::from_float(1.0), - USD_DECIMALS, - project_metadata.token_information.decimals, - ) - .unwrap(); - let jwt = get_mock_jwt_with_cid( - issuer, - InvestorType::Institutional, - generate_did_from_account(issuer), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - - inst.mint_plmc_to(vec![(issuer, 1000 * PLMC).into()]); - inst.execute(|| { - assert_ok!(Pallet::::create_project( - RuntimeOrigin::signed(issuer), - jwt.clone(), - project_metadata.clone() - )); - }); - issuer += 1; - }; - // 5 to 20 succeeds - for i in 6..=18 { - succeed_with_decimals(i); - } - } - - #[test] - fn unaccepted_prices() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut issuer = ISSUER_1; - let mut assert_price = |price: f64, fail: bool| { - inst.mint_plmc_to(vec![(issuer, 1000 * PLMC).into()]); - let mut project_metadata = default_project_metadata(issuer); - - // Need this helper function because the price provider does not allow prices below 10^-6 - let calculate_decimals_aware_price = |price: f64, decimals: u8| { - let price = PriceOf::::from_float(price); - let usd_unit = 10u128.checked_pow(USD_DECIMALS.into()).unwrap(); - let usd_price_with_decimals = price.checked_mul_int(usd_unit * 1_000_000).unwrap(); - let asset_unit = 10u128.checked_pow(decimals.into()).unwrap(); - - let divisor = FixedU128::from_float(1_000_000f64); - - FixedU128::checked_from_rational(usd_price_with_decimals, asset_unit).unwrap().div(divisor) - }; - - project_metadata.minimum_price = - calculate_decimals_aware_price(price, project_metadata.token_information.decimals); - project_metadata.total_allocation_size = - project_metadata.minimum_price.reciprocal().unwrap().saturating_mul_int(100_000 * USD_UNIT); - project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; - - let jwt = get_mock_jwt_with_cid( - issuer, - InvestorType::Institutional, - generate_did_from_account(issuer), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - - let run_extrinsic = || { - Pallet::::create_project( - RuntimeOrigin::signed(issuer), - jwt.clone(), - project_metadata.clone(), - ) - }; - inst.execute(|| { - if fail { - assert_noop!(run_extrinsic(), Error::::BadTokenomics,); - } else { - assert_ok!(run_extrinsic()); - } - }); - issuer += 1; - }; - - let low_prices = vec![0.0000001, 0.000001]; - let high_prices = vec![10_000f64, 100_000f64]; - let right_prices = vec![0.00001, 0.001, 0.01, 0.1, 1.0, 10.0, 100f64, 1_000f64]; - - for price in low_prices { - assert_price(price, true); - } - for price in high_prices { - assert_price(price, true); - } - for price in right_prices { - assert_price(price, false); - } - } - - #[test] - fn allocation_smaller_than_decimals() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.total_allocation_size = 2_000_000; - project_metadata.token_information.decimals = 8; - project_metadata.minimum_price = ::PriceProvider::calculate_decimals_aware_price( - PriceOf::::from_float(100_000.0f64), - USD_DECIMALS, - project_metadata.token_information.decimals, - ) - .unwrap(); - - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - - inst.execute(|| { - assert_noop!( - Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - ), - Error::::AllocationSizeError - ); - }); - } - } -} - -#[cfg(test)] -mod edit_project_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use polimec_common::USD_DECIMALS; - use polimec_common_test_utils::get_mock_jwt; - #[test] - fn project_id_stays_the_same() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - - project_metadata.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(15.0), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(); - assert_ok!(inst.execute(|| crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id, - project_metadata.clone() - ))); - let next_project_id = inst.execute(|| NextProjectId::::get()); - assert_eq!(project_id, next_project_id - 1); - let projects_details = inst.execute(|| ProjectsDetails::::iter_keys().collect_vec()); - let project_metadatas = inst.execute(|| ProjectsMetadata::::iter_keys().collect_vec()); - assert_eq!(projects_details, vec![project_id]); - assert_eq!(project_metadatas, vec![project_id]); - } - - #[test] - fn multiple_fields_edited() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - let mut new_metadata_1 = project_metadata.clone(); - let new_policy_hash = ipfs_hash(); - new_metadata_1.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(15.0), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(); - let new_metadata_2 = ProjectMetadataOf:: { - token_information: CurrencyMetadata { - name: BoundedVec::try_from("Changed Name".as_bytes().to_vec()).unwrap(), - symbol: BoundedVec::try_from("CN".as_bytes().to_vec()).unwrap(), - decimals: 12, - }, - mainnet_token_max_supply: 100_000_000 * CT_UNIT, - total_allocation_size: 5_000_000 * CT_UNIT, - auction_round_allocation_percentage: Percent::from_percent(30u8), - minimum_price: PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(20.0), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(), - bidding_ticket_sizes: BiddingTicketSizes { - professional: TicketSize::new(10_000 * USD_UNIT, Some(20_000 * USD_UNIT)), - institutional: TicketSize::new(20_000 * USD_UNIT, Some(30_000 * USD_UNIT)), - phantom: Default::default(), - }, - contributing_ticket_sizes: ContributingTicketSizes { - retail: TicketSize::new(1_000 * USD_UNIT, Some(2_000 * USD_UNIT)), - professional: TicketSize::new(2_000 * USD_UNIT, Some(3_000 * USD_UNIT)), - institutional: TicketSize::new(3_000 * USD_UNIT, Some(4_000 * USD_UNIT)), - phantom: Default::default(), - }, - participation_currencies: vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC] - .try_into() - .unwrap(), - - funding_destination_account: ISSUER_2, - policy_ipfs_cid: Some(new_policy_hash), - }; - - // No fields changed - assert_ok!(inst.execute(|| crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id, - project_metadata.clone() - ))); - inst.execute(|| { - find_event!(TestRuntime, Event::::MetadataEdited{project_id, ref metadata}, project_id == 0, metadata == &project_metadata); - }); - - // Just one field changed - assert_ok!(inst.execute(|| crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id, - new_metadata_1.clone() - ))); - assert_eq!(inst.get_project_metadata(project_id), new_metadata_1); - inst.execute(|| { - find_event!(TestRuntime, Event::::MetadataEdited{project_id, ref metadata}, project_id == 0, metadata == &new_metadata_1); - }); - - // All fields changed - assert_ok!(inst.execute(|| crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id, - new_metadata_2.clone() - ))); - assert_eq!(inst.get_project_metadata(project_id), new_metadata_2); - inst.execute(|| { - find_event!(TestRuntime, Event::::MetadataEdited{project_id, ref metadata}, project_id == 0, metadata == &new_metadata_2); - }); - } - - #[test] - fn adding_project_policy() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.policy_ipfs_cid = None; - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - let mut new_metadata = project_metadata.clone(); - let new_policy_hash = ipfs_hash(); - new_metadata.policy_ipfs_cid = Some(new_policy_hash); - assert_ok!(inst.execute(|| crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id, - new_metadata.clone() - ))); - assert_eq!(inst.get_project_metadata(project_id), new_metadata); - } - - #[test] - fn storage_changes() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - let mut new_metadata = project_metadata.clone(); - - let new_price = PriceOf::::from_float(1f64); - new_metadata.minimum_price = ::PriceProvider::calculate_decimals_aware_price( - new_price, - USD_DECIMALS, - new_metadata.token_information.decimals, - ) - .unwrap(); - new_metadata.total_allocation_size = 100_000 * CT_UNIT; - assert_ok!(inst.execute(|| crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id, - new_metadata.clone() - ))); - // Project details reflect changes - assert_eq!(inst.get_project_details(project_id).fundraising_target_usd, 100_000 * USD_UNIT); - // Bucket reflects changes - let new_bucket = Pallet::::create_bucket_from_metadata(&new_metadata).unwrap(); - let stored_bucket = inst.execute(|| Buckets::::get(project_id).unwrap()); - assert_eq!(stored_bucket, new_bucket); - // Event emitted - inst.execute(|| { - find_event!(TestRuntime, Event::::MetadataEdited{project_id, ref metadata}, project_id == 0, metadata == &new_metadata); - }); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn called_by_different_issuer() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let ed = inst.get_ed(); - let issuer_1_mint: UserToPLMCBalance = (ISSUER_1, ed).into(); - let issuer_2_mint: UserToPLMCBalance = (ISSUER_2, ed).into(); - - let project_metadata_1 = default_project_metadata(ISSUER_1); - let project_metadata_2 = default_project_metadata(ISSUER_2); - - inst.mint_plmc_to(vec![issuer_1_mint.clone(), issuer_2_mint.clone()]); - - let jwt_1 = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata_1.clone().policy_ipfs_cid.unwrap(), - ); - let jwt_2 = get_mock_jwt_with_cid( - ISSUER_2, - InvestorType::Institutional, - generate_did_from_account(ISSUER_2), - project_metadata_2.clone().policy_ipfs_cid.unwrap(), - ); - - let project_id_1 = inst.create_new_project(project_metadata_1.clone(), ISSUER_1, None); - let project_id_2 = inst.create_new_project(project_metadata_2.clone(), ISSUER_2, None); - - inst.execute(|| { - assert_noop!( - Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_2), - jwt_2, - project_id_1, - project_metadata_2 - ), - Error::::NotIssuer - ); - assert_noop!( - Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt_1, - project_id_2, - project_metadata_1 - ), - Error::::NotIssuer - ); - }); - } - - #[test] - fn evaluation_already_started() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - inst.start_evaluation(project_id, ISSUER_1).unwrap(); - inst.execute(|| { - assert_noop!( - crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id, - project_metadata.clone() - ), - Error::::ProjectIsFrozen - ); - }); - } - - #[test] - fn non_institutional_credential() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Retail, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - - inst.execute(|| { - assert_noop!( - crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt, - project_id, - project_metadata.clone() - ), - Error::::WrongInvestorType - ); - }); - - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Professional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - crate::Pallet::::edit_project( - RuntimeOrigin::signed(ISSUER_1), - jwt, - project_id, - project_metadata - ), - Error::::WrongInvestorType - ); - }); - } - } -} - -#[cfg(test)] -mod remove_project_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn normal_remove() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - assert_ok!(inst.execute(|| crate::Pallet::::remove_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id - ))); - inst.execute(|| { - assert!(ProjectsDetails::::get(project_id).is_none()); - assert!(ProjectsMetadata::::get(project_id).is_none()); - assert!(Buckets::::get(project_id).is_none()); - assert!(DidWithActiveProjects::::get(generate_did_from_account(ISSUER_1)).is_none()); - }); - } - - #[test] - fn can_create_after_remove() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let ed = inst.get_ed(); - let issuer_mint: UserToPLMCBalance = (ISSUER_1, ed * 2).into(); - // Create a first project - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.mint_plmc_to(vec![issuer_mint.clone()]); - inst.execute(|| { - assert_ok!(Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - )); - }); - - // Same account same did - inst.mint_plmc_to(vec![issuer_mint.clone()]); - inst.execute(|| { - assert_noop!( - Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - ), - Error::::HasActiveProject - ); - }); - - // Remove the first project - inst.execute(|| { - assert_ok!(Pallet::::remove_project(RuntimeOrigin::signed(ISSUER_1), jwt.clone(), 0)); - }); - - // Create a second project - inst.execute(|| { - assert_ok!(Pallet::::create_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_metadata.clone() - )); - }); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn non_issuer_credential() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Professional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - inst.execute(|| { - assert_noop!( - crate::Pallet::::remove_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id - ), - Error::::WrongInvestorType - ); - }); - } - - #[test] - fn different_account() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_2, - InvestorType::Institutional, - generate_did_from_account(ISSUER_2), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - - inst.execute(|| { - assert_noop!( - crate::Pallet::::remove_project( - RuntimeOrigin::signed(ISSUER_2), - jwt.clone(), - project_id - ), - Error::::NotIssuer - ); - }); - } - - #[test] - fn evaluation_already_started() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - inst.start_evaluation(project_id, ISSUER_1).unwrap(); - inst.execute(|| { - assert_noop!( - crate::Pallet::::remove_project( - RuntimeOrigin::signed(ISSUER_1), - jwt.clone(), - project_id, - ), - Error::::ProjectIsFrozen - ); - }); - } - } -} +// use super::*; +// use polimec_common::credentials::InvestorType; +// use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt_with_cid}; +// +// #[cfg(test)] +// mod round_flow { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn application_round_completed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// +// inst.create_evaluating_project(project_metadata, issuer, None); +// } +// } +// } +// +// #[cfg(test)] +// mod create_project_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use polimec_common_test_utils::get_mock_jwt_with_cid; +// +// #[test] +// fn project_id_autoincrement_works() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_1 = default_project_metadata(ISSUER_1); +// let project_2 = default_project_metadata(ISSUER_2); +// let project_3 = default_project_metadata(ISSUER_3); +// +// let created_project_1_id = inst.create_evaluating_project(project_1, ISSUER_1, None); +// let created_project_2_id = inst.create_evaluating_project(project_2, ISSUER_2, None); +// let created_project_3_id = inst.create_evaluating_project(project_3, ISSUER_3, None); +// +// assert_eq!(created_project_1_id, 0); +// assert_eq!(created_project_2_id, 1); +// assert_eq!(created_project_3_id, 2); +// } +// +// #[test] +// fn multiple_creations_different_issuers() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut issuer = ISSUER_1; +// for _ in 0..512 { +// let project_metadata = default_project_metadata(issuer); +// inst.create_evaluating_project(project_metadata, issuer, None); +// inst.advance_time(1u64).unwrap(); +// issuer += 1; +// } +// } +// +// #[test] +// fn multiple_funding_currencies() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let default_project_metadata = default_project_metadata(ISSUER_1); +// +// let mut one_currency_1 = default_project_metadata.clone(); +// one_currency_1.participation_currencies = vec![AcceptedFundingAsset::USDT].try_into().unwrap(); +// +// let mut one_currency_2 = default_project_metadata.clone(); +// one_currency_2.participation_currencies = vec![AcceptedFundingAsset::USDC].try_into().unwrap(); +// +// let mut one_currency_3 = default_project_metadata.clone(); +// one_currency_3.participation_currencies = vec![AcceptedFundingAsset::DOT].try_into().unwrap(); +// +// let mut two_currencies_1 = default_project_metadata.clone(); +// two_currencies_1.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC].try_into().unwrap(); +// +// let mut two_currencies_2 = default_project_metadata.clone(); +// two_currencies_2.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::DOT].try_into().unwrap(); +// +// let mut two_currencies_3 = default_project_metadata.clone(); +// two_currencies_3.participation_currencies = +// vec![AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT].try_into().unwrap(); +// +// let mut three_currencies = default_project_metadata.clone(); +// three_currencies.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] +// .try_into() +// .unwrap(); +// +// let projects = vec![ +// one_currency_1.clone(), +// one_currency_2.clone(), +// one_currency_3, +// two_currencies_1, +// two_currencies_2, +// two_currencies_3, +// three_currencies, +// ]; +// +// let mut issuer = ISSUER_1; +// for project in projects { +// issuer += 1; +// let issuer_mint = (issuer, 1000 * PLMC).into(); +// inst.mint_plmc_to(vec![issuer_mint]); +// assert_ok!(inst.execute(|| { +// Pallet::::do_create_project(&issuer, project, generate_did_from_account(issuer)) +// })); +// } +// } +// +// #[test] +// fn issuer_can_create_second_project_after_first_is_inactive() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer: AccountId = ISSUER_1; +// let did: Did = BoundedVec::new(); +// let project_metadata: ProjectMetadataOf = default_project_metadata(issuer); +// let jwt: UntrustedToken = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// did, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// +// let failing_bids = vec![(BIDDER_1, 1000 * CT_UNIT).into(), (BIDDER_2, 1000 * CT_UNIT).into()]; +// +// inst.mint_plmc_to(default_plmc_balances()); +// inst.mint_foreign_asset_to(default_usdt_balances()); +// +// // Cannot create 2 projects consecutively +// inst.execute(|| { +// assert_ok!(Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// ), +// Error::::HasActiveProject +// ); +// }); +// +// // A Project is "inactive" after the evaluation fails +// inst.start_evaluation(0, ISSUER_1).unwrap(); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// ), +// Error::::HasActiveProject +// ); +// }); +// inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); +// assert_eq!( +// inst.get_project_details(0).status, +// ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) +// ); +// inst.execute(|| { +// assert_ok!(Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// )); +// }); +// +// // A Project is "inactive" after the funding fails +// inst.start_evaluation(1, ISSUER_1).unwrap(); +// inst.evaluate_for_users(1, default_evaluations()).unwrap(); +// inst.start_auction(1, ISSUER_1).unwrap(); +// inst.bid_for_users(1, failing_bids).unwrap(); +// inst.start_community_funding(1).unwrap(); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// ), +// Error::::HasActiveProject +// ); +// }); +// inst.finish_funding(1, None).unwrap(); +// assert_eq!(inst.get_project_details(1).status, ProjectStatus::FundingFailed); +// inst.execute(|| { +// assert_ok!(Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// )); +// }); +// +// // A project is "inactive" after the funding succeeds +// inst.start_evaluation(2, ISSUER_1).unwrap(); +// inst.evaluate_for_users(2, default_evaluations()).unwrap(); +// inst.start_auction(2, ISSUER_1).unwrap(); +// inst.bid_for_users(2, default_bids()).unwrap(); +// inst.start_community_funding(2).unwrap(); +// inst.contribute_for_users(2, default_community_buys()).unwrap(); +// inst.start_remainder_or_end_funding(2).unwrap(); +// inst.contribute_for_users(2, default_remainder_buys()).unwrap(); +// inst.finish_funding(2, None).unwrap(); +// assert_eq!(inst.get_project_details(2).status, ProjectStatus::FundingSuccessful); +// assert_ok!(inst.execute(|| crate::Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// ))); +// } +// +// #[test] +// fn shitcoin_tokenomics() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// +// // funding target of 1000 USD at 100 trillion supply +// const QUADRILLION_SUPPLY: u128 = 100_000_000_000_000 * CT_UNIT; +// // at the lowest possible price, which makes a funding target of 1 bn USD +// const LOW_PRICE: f64 = 0.00001f64; +// +// project_metadata.mainnet_token_max_supply = QUADRILLION_SUPPLY; +// project_metadata.total_allocation_size = QUADRILLION_SUPPLY; +// project_metadata.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( +// FixedU128::from_float(LOW_PRICE), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(); +// +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_ok!(crate::Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt, +// project_metadata +// )); +// }); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn non_institutional_credential_fails() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Retail, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt, +// project_metadata.clone() +// ), +// Error::::WrongInvestorType +// ); +// }); +// +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Professional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt, +// project_metadata +// ), +// Error::::WrongInvestorType +// ); +// }); +// } +// +// #[test] +// fn did_cannot_have_2_active_projects() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let ed = inst.get_ed(); +// let issuer_mint: UserToPLMCBalance = (ISSUER_1, ed * 2).into(); +// // Create a first project +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.mint_plmc_to(vec![issuer_mint.clone()]); +// inst.execute(|| { +// assert_ok!(Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// )); +// }); +// +// // different account, same did +// let jwt = get_mock_jwt_with_cid( +// ISSUER_2, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_2), +// jwt.clone(), +// project_metadata.clone() +// ), +// Error::::HasActiveProject +// ); +// }); +// } +// +// #[test] +// fn not_enough_plmc_for_escrow_ed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let ed = inst.get_ed(); +// inst.mint_plmc_to(vec![UserToPLMCBalance::new(ISSUER_1, ed)]); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata,), +// Error::::IssuerNotEnoughFunds +// ); +// }); +// } +// +// // Invalid metadata tests: +// #[test] +// fn mainnet_supply_less_than_allocation() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.total_allocation_size = 100_000_001 * CT_UNIT; +// project_metadata.mainnet_token_max_supply = 100_000_000 * CT_UNIT; +// inst.mint_plmc_to(default_plmc_balances()); +// inst.execute(|| { +// assert_noop!( +// Pallet::::do_create_project( +// &ISSUER_1, +// project_metadata, +// generate_did_from_account(ISSUER_1), +// ), +// Error::::AllocationSizeError +// ); +// }); +// } +// +// #[test] +// fn invalid_ticket_sizes() { +// let correct_project = default_project_metadata(ISSUER_1); +// +// // min in bidding below 5k +// let mut wrong_project_1 = correct_project.clone(); +// wrong_project_1.bidding_ticket_sizes.professional = TicketSize::new(4999 * USD_UNIT, None); +// +// let mut wrong_project_2 = correct_project.clone(); +// wrong_project_2.bidding_ticket_sizes.institutional = TicketSize::new(4999 * USD_UNIT, None); +// +// let mut wrong_project_3 = correct_project.clone(); +// wrong_project_3.bidding_ticket_sizes.professional = TicketSize::new(3000 * USD_UNIT, None); +// wrong_project_3.bidding_ticket_sizes.institutional = TicketSize::new(0 * USD_UNIT, None); +// +// let mut wrong_project_4 = correct_project.clone(); +// wrong_project_4.bidding_ticket_sizes.professional = TicketSize::new(USD_UNIT, None); +// wrong_project_4.bidding_ticket_sizes.institutional = TicketSize::new(USD_UNIT, None); +// +// // min in contributing below 1 USD +// let mut wrong_project_5 = correct_project.clone(); +// wrong_project_5.contributing_ticket_sizes.retail = TicketSize::new(USD_UNIT / 2, None); +// +// let mut wrong_project_6 = correct_project.clone(); +// wrong_project_6.contributing_ticket_sizes.professional = TicketSize::new(USD_UNIT / 2, None); +// +// let mut wrong_project_7 = correct_project.clone(); +// wrong_project_7.contributing_ticket_sizes.institutional = TicketSize::new(USD_UNIT / 2, None); +// +// // min higher than max +// let mut wrong_project_8 = correct_project.clone(); +// wrong_project_8.bidding_ticket_sizes.professional = TicketSize::new(5000 * USD_UNIT, Some(4990 * USD_UNIT)); +// +// let mut wrong_project_9 = correct_project.clone(); +// wrong_project_9.bidding_ticket_sizes.institutional = +// TicketSize::new(6000 * USD_UNIT, Some(5500 * USD_UNIT)); +// +// let mut wrong_project_10 = correct_project.clone(); +// wrong_project_10.contributing_ticket_sizes.retail = TicketSize::new(6000 * USD_UNIT, Some(5500 * USD_UNIT)); +// +// let mut wrong_project_11 = correct_project.clone(); +// wrong_project_11.contributing_ticket_sizes.professional = +// TicketSize::new(6000 * USD_UNIT, Some(5500 * USD_UNIT)); +// +// let mut wrong_project_12 = correct_project.clone(); +// wrong_project_12.contributing_ticket_sizes.professional = +// TicketSize::new(6000 * USD_UNIT, Some(5500 * USD_UNIT)); +// +// let wrong_projects = vec![ +// wrong_project_1.clone(), +// wrong_project_2, +// wrong_project_3.clone(), +// wrong_project_4, +// wrong_project_5, +// wrong_project_6, +// wrong_project_7, +// wrong_project_8, +// wrong_project_9, +// wrong_project_10, +// wrong_project_11, +// wrong_project_12, +// ]; +// +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// inst.mint_plmc_to(default_plmc_balances()); +// +// for project in wrong_projects { +// let project_err = inst.execute(|| { +// Pallet::::do_create_project(&ISSUER_1, project, generate_did_from_account(ISSUER_1)) +// .unwrap_err() +// }); +// assert_eq!(project_err, Error::::TicketSizeError.into()); +// } +// } +// +// #[test] +// fn duplicated_participation_currencies() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut issuer = ISSUER_1; +// let default_project_metadata = default_project_metadata(ISSUER_1); +// +// let mut wrong_project_1 = default_project_metadata.clone(); +// wrong_project_1.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDT].try_into().unwrap(); +// +// let mut wrong_project_2 = default_project_metadata.clone(); +// wrong_project_2.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDT] +// .try_into() +// .unwrap(); +// +// let mut wrong_project_3 = default_project_metadata.clone(); +// wrong_project_3.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::USDT] +// .try_into() +// .unwrap(); +// +// let mut wrong_project_4 = default_project_metadata.clone(); +// wrong_project_4.participation_currencies = +// vec![AcceptedFundingAsset::DOT, AcceptedFundingAsset::DOT, AcceptedFundingAsset::USDC] +// .try_into() +// .unwrap(); +// +// let wrong_projects = vec![wrong_project_1, wrong_project_2, wrong_project_3, wrong_project_4]; +// for project in wrong_projects { +// issuer += 1; +// let issuer_mint = (issuer, 1000 * PLMC).into(); +// inst.mint_plmc_to(vec![issuer_mint]); +// let project_err = inst.execute(|| { +// Pallet::::do_create_project(&issuer, project, generate_did_from_account(issuer)) +// .unwrap_err() +// }); +// assert_eq!(project_err, Error::::ParticipationCurrenciesError.into()); +// } +// } +// +// #[test] +// fn price_zero() { +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.minimum_price = 0_u128.into(); +// +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// inst.mint_plmc_to(default_plmc_balances()); +// let project_err = inst.execute(|| { +// Pallet::::do_create_project( +// &ISSUER_1, +// project_metadata, +// generate_did_from_account(ISSUER_1), +// ) +// .unwrap_err() +// }); +// assert_eq!(project_err, Error::::PriceTooLow.into()); +// } +// +// #[test] +// fn allocation_zero() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.total_allocation_size = 0; +// +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata), +// Error::::AllocationSizeError +// ); +// }); +// } +// +// #[test] +// fn auction_round_percentage_zero() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.auction_round_allocation_percentage = Percent::from_percent(0); +// +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata), +// Error::::AuctionRoundPercentageError +// ); +// }); +// } +// +// #[test] +// fn target_funding_less_than_1000_usd() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.minimum_price = >::calculate_decimals_aware_price( +// PriceOf::::from_float(1.0), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(); +// project_metadata.total_allocation_size = 999 * CT_UNIT; +// project_metadata.mainnet_token_max_supply = 999 * CT_UNIT; +// +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// ), +// Error::::FundingTargetTooLow +// ); +// }); +// +// project_metadata.minimum_price = >::calculate_decimals_aware_price( +// PriceOf::::from_float(0.0001), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(); +// project_metadata.total_allocation_size = 9999999u128 * CT_UNIT; +// project_metadata.mainnet_token_max_supply = 9999999u128 * CT_UNIT; +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata), +// Error::::FundingTargetTooLow +// ); +// }); +// } +// +// #[test] +// fn unaccepted_decimal_ranges() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let mut fail_with_decimals = |decimals: u8| { +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.token_information.decimals = decimals; +// project_metadata.total_allocation_size = 100_000 * 10u128.pow(decimals.into()); +// project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; +// project_metadata.minimum_price = +// ::PriceProvider::calculate_decimals_aware_price( +// PriceOf::::from_float(10_000.0f64), +// USD_DECIMALS, +// project_metadata.token_information.decimals, +// ) +// .unwrap(); +// +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// ), +// Error::::BadDecimals +// ); +// }); +// }; +// +// // less than 6 should fail +// for i in 0..=5 { +// fail_with_decimals(i); +// } +// +// // more than 18 should fail +// for i in 19..=24 { +// fail_with_decimals(i); +// } +// +// let mut issuer = ISSUER_2; +// let mut succeed_with_decimals = |decimals: u8| { +// let mut project_metadata = default_project_metadata(issuer); +// project_metadata.token_information.decimals = decimals; +// project_metadata.total_allocation_size = 100_000 * 10u128.pow(decimals.into()); +// project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; +// project_metadata.minimum_price = +// ::PriceProvider::calculate_decimals_aware_price( +// PriceOf::::from_float(1.0), +// USD_DECIMALS, +// project_metadata.token_information.decimals, +// ) +// .unwrap(); +// let jwt = get_mock_jwt_with_cid( +// issuer, +// InvestorType::Institutional, +// generate_did_from_account(issuer), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// +// inst.mint_plmc_to(vec![(issuer, 1000 * PLMC).into()]); +// inst.execute(|| { +// assert_ok!(Pallet::::create_project( +// RuntimeOrigin::signed(issuer), +// jwt.clone(), +// project_metadata.clone() +// )); +// }); +// issuer += 1; +// }; +// // 5 to 20 succeeds +// for i in 6..=18 { +// succeed_with_decimals(i); +// } +// } +// +// #[test] +// fn unaccepted_prices() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut issuer = ISSUER_1; +// let mut assert_price = |price: f64, fail: bool| { +// inst.mint_plmc_to(vec![(issuer, 1000 * PLMC).into()]); +// let mut project_metadata = default_project_metadata(issuer); +// +// // Need this helper function because the price provider does not allow prices below 10^-6 +// let calculate_decimals_aware_price = |price: f64, decimals: u8| { +// let price = PriceOf::::from_float(price); +// let usd_unit = 10u128.checked_pow(USD_DECIMALS.into()).unwrap(); +// let usd_price_with_decimals = price.checked_mul_int(usd_unit * 1_000_000).unwrap(); +// let asset_unit = 10u128.checked_pow(decimals.into()).unwrap(); +// +// let divisor = FixedU128::from_float(1_000_000f64); +// +// FixedU128::checked_from_rational(usd_price_with_decimals, asset_unit).unwrap().div(divisor) +// }; +// +// project_metadata.minimum_price = +// calculate_decimals_aware_price(price, project_metadata.token_information.decimals); +// project_metadata.total_allocation_size = +// project_metadata.minimum_price.reciprocal().unwrap().saturating_mul_int(100_000 * USD_UNIT); +// project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; +// +// let jwt = get_mock_jwt_with_cid( +// issuer, +// InvestorType::Institutional, +// generate_did_from_account(issuer), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// +// let run_extrinsic = || { +// Pallet::::create_project( +// RuntimeOrigin::signed(issuer), +// jwt.clone(), +// project_metadata.clone(), +// ) +// }; +// inst.execute(|| { +// if fail { +// assert_noop!(run_extrinsic(), Error::::BadTokenomics,); +// } else { +// assert_ok!(run_extrinsic()); +// } +// }); +// issuer += 1; +// }; +// +// let low_prices = vec![0.0000001, 0.000001]; +// let high_prices = vec![10_000f64, 100_000f64]; +// let right_prices = vec![0.00001, 0.001, 0.01, 0.1, 1.0, 10.0, 100f64, 1_000f64]; +// +// for price in low_prices { +// assert_price(price, true); +// } +// for price in high_prices { +// assert_price(price, true); +// } +// for price in right_prices { +// assert_price(price, false); +// } +// } +// +// #[test] +// fn allocation_smaller_than_decimals() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.total_allocation_size = 2_000_000; +// project_metadata.token_information.decimals = 8; +// project_metadata.minimum_price = ::PriceProvider::calculate_decimals_aware_price( +// PriceOf::::from_float(100_000.0f64), +// USD_DECIMALS, +// project_metadata.token_information.decimals, +// ) +// .unwrap(); +// +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// ), +// Error::::AllocationSizeError +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod edit_project_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use polimec_common::USD_DECIMALS; +// use polimec_common_test_utils::get_mock_jwt; +// #[test] +// fn project_id_stays_the_same() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// +// project_metadata.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(15.0), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(); +// assert_ok!(inst.execute(|| crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id, +// project_metadata.clone() +// ))); +// let next_project_id = inst.execute(|| NextProjectId::::get()); +// assert_eq!(project_id, next_project_id - 1); +// let projects_details = inst.execute(|| ProjectsDetails::::iter_keys().collect_vec()); +// let project_metadatas = inst.execute(|| ProjectsMetadata::::iter_keys().collect_vec()); +// assert_eq!(projects_details, vec![project_id]); +// assert_eq!(project_metadatas, vec![project_id]); +// } +// +// #[test] +// fn multiple_fields_edited() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// let mut new_metadata_1 = project_metadata.clone(); +// let new_policy_hash = ipfs_hash(); +// new_metadata_1.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(15.0), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(); +// let new_metadata_2 = ProjectMetadataOf:: { +// token_information: CurrencyMetadata { +// name: BoundedVec::try_from("Changed Name".as_bytes().to_vec()).unwrap(), +// symbol: BoundedVec::try_from("CN".as_bytes().to_vec()).unwrap(), +// decimals: 12, +// }, +// mainnet_token_max_supply: 100_000_000 * CT_UNIT, +// total_allocation_size: 5_000_000 * CT_UNIT, +// auction_round_allocation_percentage: Percent::from_percent(30u8), +// minimum_price: PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(20.0), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(), +// bidding_ticket_sizes: BiddingTicketSizes { +// professional: TicketSize::new(10_000 * USD_UNIT, Some(20_000 * USD_UNIT)), +// institutional: TicketSize::new(20_000 * USD_UNIT, Some(30_000 * USD_UNIT)), +// phantom: Default::default(), +// }, +// contributing_ticket_sizes: ContributingTicketSizes { +// retail: TicketSize::new(1_000 * USD_UNIT, Some(2_000 * USD_UNIT)), +// professional: TicketSize::new(2_000 * USD_UNIT, Some(3_000 * USD_UNIT)), +// institutional: TicketSize::new(3_000 * USD_UNIT, Some(4_000 * USD_UNIT)), +// phantom: Default::default(), +// }, +// participation_currencies: vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC] +// .try_into() +// .unwrap(), +// +// funding_destination_account: ISSUER_2, +// policy_ipfs_cid: Some(new_policy_hash), +// }; +// +// // No fields changed +// assert_ok!(inst.execute(|| crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id, +// project_metadata.clone() +// ))); +// inst.execute(|| { +// find_event!(TestRuntime, Event::::MetadataEdited{project_id, ref metadata}, project_id == 0, metadata == &project_metadata); +// }); +// +// // Just one field changed +// assert_ok!(inst.execute(|| crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id, +// new_metadata_1.clone() +// ))); +// assert_eq!(inst.get_project_metadata(project_id), new_metadata_1); +// inst.execute(|| { +// find_event!(TestRuntime, Event::::MetadataEdited{project_id, ref metadata}, project_id == 0, metadata == &new_metadata_1); +// }); +// +// // All fields changed +// assert_ok!(inst.execute(|| crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id, +// new_metadata_2.clone() +// ))); +// assert_eq!(inst.get_project_metadata(project_id), new_metadata_2); +// inst.execute(|| { +// find_event!(TestRuntime, Event::::MetadataEdited{project_id, ref metadata}, project_id == 0, metadata == &new_metadata_2); +// }); +// } +// +// #[test] +// fn adding_project_policy() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.policy_ipfs_cid = None; +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// let mut new_metadata = project_metadata.clone(); +// let new_policy_hash = ipfs_hash(); +// new_metadata.policy_ipfs_cid = Some(new_policy_hash); +// assert_ok!(inst.execute(|| crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id, +// new_metadata.clone() +// ))); +// assert_eq!(inst.get_project_metadata(project_id), new_metadata); +// } +// +// #[test] +// fn storage_changes() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// let mut new_metadata = project_metadata.clone(); +// +// let new_price = PriceOf::::from_float(1f64); +// new_metadata.minimum_price = ::PriceProvider::calculate_decimals_aware_price( +// new_price, +// USD_DECIMALS, +// new_metadata.token_information.decimals, +// ) +// .unwrap(); +// new_metadata.total_allocation_size = 100_000 * CT_UNIT; +// assert_ok!(inst.execute(|| crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id, +// new_metadata.clone() +// ))); +// // Project details reflect changes +// assert_eq!(inst.get_project_details(project_id).fundraising_target_usd, 100_000 * USD_UNIT); +// // Bucket reflects changes +// let new_bucket = Pallet::::create_bucket_from_metadata(&new_metadata).unwrap(); +// let stored_bucket = inst.execute(|| Buckets::::get(project_id).unwrap()); +// assert_eq!(stored_bucket, new_bucket); +// // Event emitted +// inst.execute(|| { +// find_event!(TestRuntime, Event::::MetadataEdited{project_id, ref metadata}, project_id == 0, metadata == &new_metadata); +// }); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn called_by_different_issuer() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let ed = inst.get_ed(); +// let issuer_1_mint: UserToPLMCBalance = (ISSUER_1, ed).into(); +// let issuer_2_mint: UserToPLMCBalance = (ISSUER_2, ed).into(); +// +// let project_metadata_1 = default_project_metadata(ISSUER_1); +// let project_metadata_2 = default_project_metadata(ISSUER_2); +// +// inst.mint_plmc_to(vec![issuer_1_mint.clone(), issuer_2_mint.clone()]); +// +// let jwt_1 = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata_1.clone().policy_ipfs_cid.unwrap(), +// ); +// let jwt_2 = get_mock_jwt_with_cid( +// ISSUER_2, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_2), +// project_metadata_2.clone().policy_ipfs_cid.unwrap(), +// ); +// +// let project_id_1 = inst.create_new_project(project_metadata_1.clone(), ISSUER_1, None); +// let project_id_2 = inst.create_new_project(project_metadata_2.clone(), ISSUER_2, None); +// +// inst.execute(|| { +// assert_noop!( +// Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_2), +// jwt_2, +// project_id_1, +// project_metadata_2 +// ), +// Error::::NotIssuer +// ); +// assert_noop!( +// Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt_1, +// project_id_2, +// project_metadata_1 +// ), +// Error::::NotIssuer +// ); +// }); +// } +// +// #[test] +// fn evaluation_already_started() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// inst.start_evaluation(project_id, ISSUER_1).unwrap(); +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id, +// project_metadata.clone() +// ), +// Error::::ProjectIsFrozen +// ); +// }); +// } +// +// #[test] +// fn non_institutional_credential() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Retail, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt, +// project_id, +// project_metadata.clone() +// ), +// Error::::WrongInvestorType +// ); +// }); +// +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Professional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::edit_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt, +// project_id, +// project_metadata +// ), +// Error::::WrongInvestorType +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod remove_project_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn normal_remove() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// assert_ok!(inst.execute(|| crate::Pallet::::remove_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id +// ))); +// inst.execute(|| { +// assert!(ProjectsDetails::::get(project_id).is_none()); +// assert!(ProjectsMetadata::::get(project_id).is_none()); +// assert!(Buckets::::get(project_id).is_none()); +// assert!(DidWithActiveProjects::::get(generate_did_from_account(ISSUER_1)).is_none()); +// }); +// } +// +// #[test] +// fn can_create_after_remove() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let ed = inst.get_ed(); +// let issuer_mint: UserToPLMCBalance = (ISSUER_1, ed * 2).into(); +// // Create a first project +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.mint_plmc_to(vec![issuer_mint.clone()]); +// inst.execute(|| { +// assert_ok!(Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// )); +// }); +// +// // Same account same did +// inst.mint_plmc_to(vec![issuer_mint.clone()]); +// inst.execute(|| { +// assert_noop!( +// Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// ), +// Error::::HasActiveProject +// ); +// }); +// +// // Remove the first project +// inst.execute(|| { +// assert_ok!(Pallet::::remove_project(RuntimeOrigin::signed(ISSUER_1), jwt.clone(), 0)); +// }); +// +// // Create a second project +// inst.execute(|| { +// assert_ok!(Pallet::::create_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_metadata.clone() +// )); +// }); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn non_issuer_credential() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Professional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::remove_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id +// ), +// Error::::WrongInvestorType +// ); +// }); +// } +// +// #[test] +// fn different_account() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_2, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_2), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::remove_project( +// RuntimeOrigin::signed(ISSUER_2), +// jwt.clone(), +// project_id +// ), +// Error::::NotIssuer +// ); +// }); +// } +// +// #[test] +// fn evaluation_already_started() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.mint_plmc_to(default_plmc_balances()); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// inst.start_evaluation(project_id, ISSUER_1).unwrap(); +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::remove_project( +// RuntimeOrigin::signed(ISSUER_1), +// jwt.clone(), +// project_id, +// ), +// Error::::ProjectIsFrozen +// ); +// }); +// } +// } +// } diff --git a/pallets/funding/src/tests/2_evaluation.rs b/pallets/funding/src/tests/2_evaluation.rs index 20c3b210e..d51696b39 100644 --- a/pallets/funding/src/tests/2_evaluation.rs +++ b/pallets/funding/src/tests/2_evaluation.rs @@ -1,1010 +1,1010 @@ -use super::*; - -#[cfg(test)] -mod round_flow { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use std::collections::HashSet; - - #[test] - fn evaluation_round_completed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = default_evaluations(); - - inst.create_auctioning_project(project_metadata, issuer, None, evaluations); - } - - #[test] - fn multiple_evaluating_projects() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project1 = default_project_metadata(ISSUER_1); - let project2 = default_project_metadata(ISSUER_2); - let project3 = default_project_metadata(ISSUER_3); - let project4 = default_project_metadata(ISSUER_4); - let evaluations = default_evaluations(); - - inst.create_auctioning_project(project1, ISSUER_1, None, evaluations.clone()); - inst.create_auctioning_project(project2, ISSUER_2, None, evaluations.clone()); - inst.create_auctioning_project(project3, ISSUER_3, None, evaluations.clone()); - inst.create_auctioning_project(project4, ISSUER_4, None, evaluations); - } - - #[test] - fn plmc_price_change_doesnt_affect_evaluation_end() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - - // Decreasing the price before the end doesn't make a project over the threshold fail. - let target_funding = - project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size); - let target_evaluation_usd = Percent::from_percent(10) * target_funding; - - let evaluations = vec![(EVALUATOR_1, target_evaluation_usd).into()]; - let evaluation_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - inst.mint_plmc_to(evaluation_plmc); - - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - inst.evaluate_for_users(project_id, evaluations.clone()).unwrap(); - - let old_price = ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap(); - PRICE_MAP.with_borrow_mut(|map| map.insert(PLMC_FOREIGN_ID, old_price / 2.into())); - - inst.start_auction(project_id, ISSUER_1).unwrap(); - - // Increasing the price before the end doesn't make a project under the threshold succeed. - let evaluations = vec![(EVALUATOR_1, target_evaluation_usd / 2).into()]; - let evaluation_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - inst.mint_plmc_to(evaluation_plmc); - - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_2, None); - inst.evaluate_for_users(project_id, evaluations.clone()).unwrap(); - - let old_price = ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap(); - PRICE_MAP.with_borrow_mut(|map| map.insert(PLMC_FOREIGN_ID, old_price * 2.into())); - - let update_block = inst.get_update_block(project_id, &UpdateType::EvaluationEnd).unwrap(); - let now = inst.current_block(); - inst.advance_time(update_block - now + 1).unwrap(); - let project_status = inst.get_project_details(project_id).status; - assert_eq!(project_status, ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed)); - } - - #[test] - fn different_decimals_ct_works_as_expected() { - // Setup some base values to compare different decimals - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let ed = inst.get_ed(); - let default_project_metadata = default_project_metadata(ISSUER_1); - let original_decimal_aware_price = default_project_metadata.minimum_price; - let original_price = ::PriceProvider::convert_back_to_normal_price( - original_decimal_aware_price, - USD_DECIMALS, - default_project_metadata.token_information.decimals, - ) - .unwrap(); - let min_evaluation_amount_usd = ::MinUsdPerEvaluation::get(); - let stored_plmc_price = - inst.execute(|| ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap()); - let usable_plmc_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - PLMC_FOREIGN_ID, - USD_DECIMALS, - PLMC_DECIMALS, - ) - .unwrap() - }); - let min_evaluation_amount_plmc = - usable_plmc_price.reciprocal().unwrap().checked_mul_int(min_evaluation_amount_usd).unwrap(); - - // Test independent of CT decimals - Right PLMC conversion is stored. - // We move comma 4 places to the left since PLMC has 4 more decimals than USD. - assert_eq!(stored_plmc_price, FixedU128::from_float(8.4)); - assert_eq!(usable_plmc_price, FixedU128::from_float(0.00084)); - - let mut evaluation_ct_thresholds = Vec::new(); - let mut evaluation_usd_thresholds = Vec::new(); - let mut evaluation_plmc_thresholds = Vec::new(); - - let mut decimal_test = |decimals: u8| { - let mut project_metadata = default_project_metadata.clone(); - project_metadata.token_information.decimals = decimals; - project_metadata.minimum_price = - ::PriceProvider::calculate_decimals_aware_price( - original_price, - USD_DECIMALS, - decimals, - ) - .unwrap(); - project_metadata.total_allocation_size = 1_000_000 * 10u128.pow(decimals as u32); - project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; - - let issuer: AccountIdOf = (10_000 + inst.get_new_nonce()).try_into().unwrap(); - let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - - let evaluation_threshold = inst.execute(|| ::EvaluationSuccessThreshold::get()); - let evaluation_threshold_ct = evaluation_threshold * project_metadata.total_allocation_size; - evaluation_ct_thresholds.push(evaluation_threshold_ct); - - let evaluation_threshold_usd = - project_metadata.minimum_price.saturating_mul_int(evaluation_threshold_ct); - evaluation_usd_thresholds.push(evaluation_threshold_usd); - - let evaluation_threshold_plmc = - usable_plmc_price.reciprocal().unwrap().checked_mul_int(evaluation_threshold_usd).unwrap(); - evaluation_plmc_thresholds.push(evaluation_threshold_plmc); - - // CT price should be multiplied or divided by the amount of decimal difference with USD. - let decimal_abs_diff = USD_DECIMALS.abs_diff(decimals); - let original_price_as_usd = original_price.saturating_mul_int(10u128.pow(USD_DECIMALS as u32)); - let min_price_as_usd = project_metadata.minimum_price.saturating_mul_int(USD_UNIT); - if decimals < USD_DECIMALS { - assert_eq!(min_price_as_usd, original_price_as_usd * 10u128.pow(decimal_abs_diff as u32)); - } else { - assert_eq!(min_price_as_usd, original_price_as_usd / 10u128.pow(decimal_abs_diff as u32)); - } - - // A minimum evaluation goes through. This is a fixed USD/PLMC value, so independent of CT decimals. - inst.mint_plmc_to(vec![UserToPLMCBalance::new(EVALUATOR_1, min_evaluation_amount_plmc + ed)]); - assert_ok!(inst.execute(|| PolimecFunding::evaluate( - RuntimeOrigin::signed(EVALUATOR_1), - get_mock_jwt_with_cid( - EVALUATOR_1, - InvestorType::Retail, - generate_did_from_account(EVALUATOR_1), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - min_evaluation_amount_usd - ))); - - // Try bonding up to the threshold with a second evaluation - inst.mint_plmc_to(vec![UserToPLMCBalance::new( - EVALUATOR_2, - evaluation_threshold_plmc + ed - min_evaluation_amount_plmc, - )]); - assert_ok!(inst.execute(|| PolimecFunding::evaluate( - RuntimeOrigin::signed(EVALUATOR_2), - get_mock_jwt_with_cid( - EVALUATOR_2, - InvestorType::Retail, - generate_did_from_account(EVALUATOR_2), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluation_threshold_usd - min_evaluation_amount_usd - ))); - - // The evaluation should succeed when we bond the threshold PLMC amount in total. - inst.start_auction(project_id, issuer).unwrap(); - }; - - for decimals in 6..=18 { - decimal_test(decimals); - } - - // Since we use the same original price and allocation size and adjust for decimals, - // the USD and PLMC amounts should be the same - assert!(evaluation_usd_thresholds.iter().all(|x| *x == evaluation_usd_thresholds[0])); - assert!(evaluation_plmc_thresholds.iter().all(|x| *x == evaluation_plmc_thresholds[0])); - - // CT amounts however should be different from each other - let mut hash_set = HashSet::new(); - for amount in evaluation_ct_thresholds { - assert!(!hash_set.contains(&amount)); - hash_set.insert(amount); - } - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn round_fails_after_not_enough_bonds() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let now = inst.current_block(); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = default_failing_evaluations(); - let plmc_eval_deposits: Vec> = - inst.calculate_evaluation_plmc_spent(evaluations, false); - let plmc_existential_deposits = plmc_eval_deposits.accounts().existential_deposits(); - - let expected_evaluator_balances = inst.generic_map_operation( - vec![plmc_eval_deposits.clone(), plmc_existential_deposits.clone()], - MergeOperation::Add, - ); - - inst.mint_plmc_to(plmc_eval_deposits.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - let project_id = inst.create_evaluating_project(project_metadata, issuer, None); - - let evaluation_end = inst - .get_project_details(project_id) - .round_duration - .end() - .expect("Evaluation round end block should be set"); - - inst.evaluate_for_users(project_id, default_failing_evaluations()).expect("Bonding should work"); - - inst.do_free_plmc_assertions(plmc_existential_deposits); - inst.do_reserved_plmc_assertions(plmc_eval_deposits, HoldReason::Evaluation(project_id).into()); - - inst.advance_time(evaluation_end - now + 1).unwrap(); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - inst.settle_project(project_id).unwrap(); - inst.do_free_plmc_assertions(expected_evaluator_balances); - } - } -} - -#[cfg(test)] -mod start_evaluation_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn evaluation_starts() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - - let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); - let jwt = get_mock_jwt_with_cid( - issuer, - InvestorType::Institutional, - generate_did_from_account(issuer), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); - assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( - RuntimeOrigin::signed(issuer), - jwt, - project_id - ))); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationRound); - } - - #[test] - fn storage_is_updated() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let issuer_did = generate_did_from_account(issuer); - let project_metadata = default_project_metadata(issuer); - - let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); - let jwt = get_mock_jwt_with_cid( - issuer, - InvestorType::Institutional, - issuer_did.clone(), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let expected_details = ProjectDetailsOf:: { - issuer_account: ISSUER_1, - issuer_did, - is_frozen: true, - weighted_average_price: None, - status: ProjectStatus::EvaluationRound, - round_duration: BlockNumberPair::new(None, None), - random_end_block: None, - fundraising_target_usd: project_metadata - .minimum_price - .saturating_mul_int(project_metadata.total_allocation_size), - remaining_contribution_tokens: project_metadata.total_allocation_size, - funding_amount_reached_usd: 0u128, - evaluation_round_info: EvaluationRoundInfoOf:: { - total_bonded_usd: 0u128, - total_bonded_plmc: 0u128, - evaluators_outcome: EvaluatorsOutcome::Unchanged, - }, - usd_bid_on_oversubscription: None, - funding_end_block: None, - migration_type: None, - }; - assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( - RuntimeOrigin::signed(issuer), - jwt, - project_id - ))); - - assert_eq!(inst.get_project_details(project_id), expected_details); - } - } - - #[cfg(test)] - mod failure { - use super::*; - use polimec_common_test_utils::get_mock_jwt; - - #[test] - fn non_institutional_jwt() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - - let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); - - inst.execute(|| { - assert_noop!( - PolimecFunding::start_evaluation( - RuntimeOrigin::signed(issuer), - get_mock_jwt_with_cid( - issuer, - InvestorType::Professional, - generate_did_from_account(issuer), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - ), - Error::::WrongInvestorType - ); - }); - - inst.execute(|| { - assert_noop!( - PolimecFunding::start_evaluation( - RuntimeOrigin::signed(issuer), - get_mock_jwt_with_cid( - issuer, - InvestorType::Retail, - generate_did_from_account(issuer), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - ), - Error::::WrongInvestorType - ); - }); - } - - #[test] - fn evaluation_started_already() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - - let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); - let jwt = get_mock_jwt_with_cid( - issuer, - InvestorType::Institutional, - generate_did_from_account(issuer), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); - assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( - RuntimeOrigin::signed(issuer), - jwt.clone(), - project_id - ))); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationRound); - - inst.execute(|| { - assert_noop!( - PolimecFunding::start_evaluation(RuntimeOrigin::signed(issuer), jwt, project_id), - Error::::IncorrectRound - ); - }); - } - - #[test] - fn no_policy_provided() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let mut project_metadata = default_project_metadata(issuer); - project_metadata.policy_ipfs_cid = None; - - let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); - let jwt = get_mock_jwt(issuer, InvestorType::Institutional, generate_did_from_account(issuer)); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); - inst.execute(|| { - assert_noop!( - PolimecFunding::start_evaluation(RuntimeOrigin::signed(issuer), jwt, project_id), - Error::::CidNotProvided - ); - }); - } - - #[test] - fn different_account() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - - let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); - let jwt = get_mock_jwt_with_cid( - ISSUER_1, - InvestorType::Institutional, - generate_did_from_account(ISSUER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); - assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( - RuntimeOrigin::signed(ISSUER_1), - jwt, - project_id - ))); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationRound); - - inst.execute(|| { - assert_noop!( - PolimecFunding::start_evaluation( - RuntimeOrigin::signed(ISSUER_2), - get_mock_jwt_with_cid( - ISSUER_2, - InvestorType::Institutional, - generate_did_from_account(ISSUER_2), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - ), - Error::::NotIssuer - ); - }); - } - } -} - -#[cfg(test)] -mod evaluate_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use frame_support::traits::fungible::InspectFreeze; - use pallet_balances::AccountData; - - #[test] - fn all_investor_types() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - - let evaluations = vec![ - (EVALUATOR_1, 500 * USD_UNIT).into(), - (EVALUATOR_2, 1000 * USD_UNIT).into(), - (EVALUATOR_3, 20_000 * USD_UNIT).into(), - ]; - let necessary_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - - inst.mint_plmc_to(necessary_plmc); - - assert_ok!(inst.execute(|| PolimecFunding::evaluate( - RuntimeOrigin::signed(evaluations[0].account), - get_mock_jwt_with_cid( - evaluations[0].account, - InvestorType::Institutional, - generate_did_from_account(evaluations[0].account), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluations[0].usd_amount, - ))); - - assert_ok!(inst.execute(|| PolimecFunding::evaluate( - RuntimeOrigin::signed(evaluations[1].account), - get_mock_jwt_with_cid( - evaluations[1].account, - InvestorType::Professional, - generate_did_from_account(evaluations[1].account), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluations[1].usd_amount, - ))); - - assert_ok!(inst.execute(|| PolimecFunding::evaluate( - RuntimeOrigin::signed(evaluations[2].account), - get_mock_jwt_with_cid( - evaluations[2].account, - InvestorType::Retail, - generate_did_from_account(evaluations[2].account), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluations[2].usd_amount, - ))); - } - - #[test] - fn using_frozen_tokens() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - - let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * USD_UNIT); - let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], true); - - inst.mint_plmc_to(necessary_plmc.clone()); - - inst.execute(|| { - mock::Balances::set_freeze(&(), &EVALUATOR_1, necessary_plmc[0].plmc_amount).unwrap(); - }); - - assert_ok!(inst.execute(|| PolimecFunding::evaluate( - RuntimeOrigin::signed(evaluation.account), - get_mock_jwt_with_cid( - evaluation.account, - InvestorType::Retail, - generate_did_from_account(evaluation.account), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluation.usd_amount - ))); - } - - #[test] - fn storage_check() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * USD_UNIT); - let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], false); - let plmc_existential_deposits = necessary_plmc.accounts().existential_deposits(); - - inst.mint_plmc_to(necessary_plmc.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - inst.execute(|| { - assert_eq!(Evaluations::::iter_values().collect_vec(), vec![]); - }); - - let did = generate_did_from_account(evaluation.account); - - assert_ok!(inst.execute(|| PolimecFunding::evaluate( - RuntimeOrigin::signed(evaluation.account), - get_mock_jwt_with_cid( - evaluation.account, - InvestorType::Retail, - did.clone(), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluation.usd_amount - ))); - - inst.execute(|| { - let evaluations = Evaluations::::iter_prefix_values((project_id,)).collect_vec(); - assert_eq!(evaluations.len(), 1); - let stored_evaluation = &evaluations[0]; - let expected_evaluation_item = EvaluationInfoOf:: { - id: 0, - did, - project_id: 0, - evaluator: EVALUATOR_1, - original_plmc_bond: necessary_plmc[0].plmc_amount, - current_plmc_bond: necessary_plmc[0].plmc_amount, - early_usd_amount: evaluation.usd_amount, - late_usd_amount: 0, - when: 1, - }; - assert_eq!(stored_evaluation, &expected_evaluation_item); - }); - } - - #[test] - fn can_evaluate_with_frozen_tokens() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - - let evaluation = UserToUSDBalance::new(EVALUATOR_4, 1_000_000 * USD_UNIT); - let plmc_required = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], false); - let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); - let frozen_amount = plmc_required[0].plmc_amount; - - inst.mint_plmc_to(plmc_required.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - inst.execute(|| { - mock::Balances::set_freeze(&(), &EVALUATOR_4, plmc_required[0].plmc_amount).unwrap(); - }); - - inst.execute(|| { - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(EVALUATOR_4), ISSUER_1, frozen_amount,), - TokenError::Frozen - ); - }); - - let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - inst.execute(|| { - assert_ok!(PolimecFunding::evaluate( - RuntimeOrigin::signed(EVALUATOR_4), - get_mock_jwt_with_cid( - EVALUATOR_4, - InvestorType::Retail, - generate_did_from_account(EVALUATOR_4), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluation.usd_amount - )); - }); - - let new_evaluations = default_evaluations(); - let new_plmc_required = inst.calculate_evaluation_plmc_spent(new_evaluations.clone(), true); - inst.mint_plmc_to(new_plmc_required.clone()); - inst.evaluate_for_users(project_id, new_evaluations).unwrap(); - - inst.start_auction(project_id, ISSUER_1).unwrap(); - inst.start_community_funding(project_id).unwrap(); - inst.start_remainder_or_end_funding(project_id).unwrap(); - inst.finish_funding(project_id, None).unwrap(); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - - let free_balance = inst.get_free_plmc_balance_for(EVALUATOR_4); - let evaluation_held_balance = - inst.get_reserved_plmc_balance_for(EVALUATOR_4, HoldReason::Evaluation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &EVALUATOR_4)); - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(evaluation_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - - let treasury_account = ::BlockchainOperationTreasury::get(); - let pre_slash_treasury_balance = inst.get_free_plmc_balance_for(treasury_account); - - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - inst.execute(|| { - PolimecFunding::settle_failed_evaluation( - RuntimeOrigin::signed(EVALUATOR_4), - project_id, - EVALUATOR_4, - 0, - ) - .unwrap(); - }); - - let post_slash_treasury_balance = inst.get_free_plmc_balance_for(treasury_account); - let free_balance = inst.get_free_plmc_balance_for(EVALUATOR_4); - let evaluation_held_balance = - inst.get_reserved_plmc_balance_for(EVALUATOR_4, HoldReason::Evaluation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &EVALUATOR_4)); - let account_data = inst.execute(|| System::account(&EVALUATOR_4)).data; - - let post_slash_evaluation_plmc = - frozen_amount - (::EvaluatorSlash::get() * frozen_amount); - assert_eq!(free_balance, inst.get_ed() + post_slash_evaluation_plmc); - assert_eq!(evaluation_held_balance, Zero::zero()); - assert_eq!(frozen_balance, frozen_amount); - let expected_account_data = AccountData { - free: inst.get_ed() + post_slash_evaluation_plmc, - reserved: Zero::zero(), - frozen: frozen_amount, - flags: Default::default(), - }; - assert_eq!(account_data, expected_account_data); - - assert!(account_data.frozen > account_data.free); - assert_eq!( - post_slash_treasury_balance, - pre_slash_treasury_balance + ::EvaluatorSlash::get() * frozen_amount - ); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn project_is_not_in_evaluation_round() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = - inst.create_auctioning_project(project_metadata.clone(), issuer, None, default_evaluations()); - - inst.execute(|| { - assert_noop!( - PolimecFunding::evaluate( - RuntimeOrigin::signed(EVALUATOR_1), - get_mock_jwt_with_cid( - EVALUATOR_1, - InvestorType::Retail, - generate_did_from_account(EVALUATOR_1), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - 500 * USD_UNIT, - ), - Error::::IncorrectRound - ); - }); - } - - #[test] - fn insufficient_plmc_for_desired_evaluation() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = default_evaluations(); - let insufficient_eval_deposits = inst - .calculate_evaluation_plmc_spent(evaluations.clone(), false) - .iter() - .map(|UserToPLMCBalance { account, plmc_amount }| UserToPLMCBalance::new(*account, plmc_amount / 2)) - .collect::>>(); - - let plmc_existential_deposits = insufficient_eval_deposits.accounts().existential_deposits(); - - inst.mint_plmc_to(insufficient_eval_deposits); - inst.mint_plmc_to(plmc_existential_deposits); - - let project_id = inst.create_evaluating_project(project_metadata, issuer, None); - - let dispatch_error = inst.evaluate_for_users(project_id, evaluations); - assert_err!(dispatch_error, TokenError::FundsUnavailable) - } - - #[test] - fn evaluation_placing_user_balance_under_ed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = vec![UserToUSDBalance::new(EVALUATOR_1, 1000 * USD_UNIT)]; - let evaluating_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), false); - let mut plmc_insufficient_existential_deposit = evaluating_plmc.accounts().existential_deposits(); - - plmc_insufficient_existential_deposit[0].plmc_amount = - plmc_insufficient_existential_deposit[0].plmc_amount / 2; - - inst.mint_plmc_to(evaluating_plmc); - inst.mint_plmc_to(plmc_insufficient_existential_deposit); - - let project_id = inst.create_evaluating_project(project_metadata, issuer, None); - - let dispatch_error = inst.evaluate_for_users(project_id, evaluations); - assert_err!(dispatch_error, TokenError::FundsUnavailable) - } - - #[test] - fn cannot_evaluate_more_than_project_limit() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let evaluations = (0u32..::MaxEvaluationsPerProject::get()) - .map(|i| UserToUSDBalance::::new(i as u32 + 420u32, (100u128 * CT_UNIT).into())) - .collect_vec(); - let failing_evaluation = UserToUSDBalance::new(EVALUATOR_1, 1000 * CT_UNIT); - - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - - let plmc_for_evaluating = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - - inst.mint_plmc_to(plmc_for_evaluating.clone()); - - inst.evaluate_for_users(project_id, evaluations.clone()).unwrap(); - - let plmc_for_failing_evaluating = - inst.calculate_evaluation_plmc_spent(vec![failing_evaluation.clone()], true); - - inst.mint_plmc_to(plmc_for_failing_evaluating.clone()); - - assert_err!( - inst.evaluate_for_users(project_id, vec![failing_evaluation]), - Error::::TooManyProjectParticipations - ); - } - - #[test] - fn cannot_evaluate_more_than_user_limit() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let evaluations = (0u32..::MaxEvaluationsPerUser::get()) - .map(|_| UserToUSDBalance::::new(EVALUATOR_1, (100u128 * USD_UNIT).into())) - .collect_vec(); - let failing_evaluation = UserToUSDBalance::new(EVALUATOR_1, 100 * USD_UNIT); - - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - - let plmc_for_evaluating = inst.calculate_evaluation_plmc_spent(evaluations.clone(), false); - let plmc_existential_deposits = evaluations.accounts().existential_deposits(); - - inst.mint_plmc_to(plmc_for_evaluating.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - inst.evaluate_for_users(project_id, evaluations.clone()).unwrap(); - - let plmc_for_failing_evaluating = - inst.calculate_evaluation_plmc_spent(vec![failing_evaluation.clone()], true); - - inst.mint_plmc_to(plmc_for_failing_evaluating.clone()); - - assert_err!( - inst.evaluate_for_users(project_id, vec![failing_evaluation]), - Error::::TooManyUserParticipations - ); - } - - #[test] - fn cannot_use_balance_on_hold() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - - let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * USD_UNIT); - let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], false); - let ed = necessary_plmc.accounts().existential_deposits(); - - inst.mint_plmc_to(necessary_plmc.clone()); - inst.mint_plmc_to(ed.clone()); - - inst.execute(|| { - ::NativeCurrency::hold( - &RuntimeHoldReason::PolimecFunding(HoldReason::Evaluation(69)), - &EVALUATOR_1, - necessary_plmc[0].plmc_amount, - ) - .unwrap(); - }); - - inst.execute(|| { - assert_noop!( - PolimecFunding::evaluate( - RuntimeOrigin::signed(evaluation.account), - get_mock_jwt_with_cid( - evaluation.account, - InvestorType::Retail, - generate_did_from_account(evaluation.account), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluation.usd_amount, - ), - TokenError::FundsUnavailable - ); - }); - } - - #[test] - fn issuer_cannot_evaluate_his_project() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - assert_err!( - inst.execute(|| crate::Pallet::::do_evaluate( - &(&ISSUER_1 + 1), - project_id, - 500 * USD_UNIT, - generate_did_from_account(ISSUER_1), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )), - Error::::ParticipationToOwnProject - ); - } - - #[test] - fn cannot_use_same_plmc_for_2_evaluations() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - - let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * USD_UNIT); - let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], true); - - inst.mint_plmc_to(necessary_plmc.clone()); - - inst.execute(|| { - assert_ok!(PolimecFunding::evaluate( - RuntimeOrigin::signed(evaluation.account), - get_mock_jwt_with_cid( - evaluation.account, - InvestorType::Retail, - generate_did_from_account(evaluation.account), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluation.usd_amount, - )); - }); - - inst.execute(|| { - assert_noop!( - PolimecFunding::evaluate( - RuntimeOrigin::signed(evaluation.account), - get_mock_jwt_with_cid( - evaluation.account, - InvestorType::Retail, - generate_did_from_account(evaluation.account), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - evaluation.usd_amount, - ), - TokenError::FundsUnavailable - ); - }); - } - - #[test] - fn cannot_evaluate_with_less_than_100_usd() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - let evaluator = EVALUATOR_1; - let jwt = get_mock_jwt_with_cid( - evaluator, - InvestorType::Retail, - generate_did_from_account(evaluator), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - - inst.mint_plmc_to(vec![(evaluator.clone(), 2000 * PLMC).into()]); - - // Cannot evaluate with 0 USD - inst.execute(|| { - assert_noop!( - Pallet::::evaluate( - RuntimeOrigin::signed(evaluator.clone()), - jwt.clone(), - project_id, - 0 - ), - Error::::TooLow - ); - }); - - // Cannot evaluate with less than 99 USD - inst.execute(|| { - assert_noop!( - Pallet::::evaluate( - RuntimeOrigin::signed(evaluator.clone()), - jwt.clone(), - project_id, - 99 * USD_UNIT - ), - Error::::TooLow - ); - }); - } - - #[test] - fn wrong_policy_on_jwt() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - - inst.execute(|| { - assert_noop!( - PolimecFunding::evaluate( - RuntimeOrigin::signed(EVALUATOR_1), - get_mock_jwt_with_cid( - EVALUATOR_1, - InvestorType::Retail, - generate_did_from_account(EVALUATOR_1), - "wrong_cid".as_bytes().to_vec().try_into().unwrap() - ), - project_id, - 500 * USD_UNIT, - ), - Error::::PolicyMismatch - ); - }); - } - } -} +// use super::*; +// +// #[cfg(test)] +// mod round_flow { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use std::collections::HashSet; +// +// #[test] +// fn evaluation_round_completed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = default_evaluations(); +// +// inst.create_auctioning_project(project_metadata, issuer, None, evaluations); +// } +// +// #[test] +// fn multiple_evaluating_projects() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project1 = default_project_metadata(ISSUER_1); +// let project2 = default_project_metadata(ISSUER_2); +// let project3 = default_project_metadata(ISSUER_3); +// let project4 = default_project_metadata(ISSUER_4); +// let evaluations = default_evaluations(); +// +// inst.create_auctioning_project(project1, ISSUER_1, None, evaluations.clone()); +// inst.create_auctioning_project(project2, ISSUER_2, None, evaluations.clone()); +// inst.create_auctioning_project(project3, ISSUER_3, None, evaluations.clone()); +// inst.create_auctioning_project(project4, ISSUER_4, None, evaluations); +// } +// +// #[test] +// fn plmc_price_change_doesnt_affect_evaluation_end() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// +// // Decreasing the price before the end doesn't make a project over the threshold fail. +// let target_funding = +// project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size); +// let target_evaluation_usd = Percent::from_percent(10) * target_funding; +// +// let evaluations = vec![(EVALUATOR_1, target_evaluation_usd).into()]; +// let evaluation_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// inst.mint_plmc_to(evaluation_plmc); +// +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// inst.evaluate_for_users(project_id, evaluations.clone()).unwrap(); +// +// let old_price = ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap(); +// PRICE_MAP.with_borrow_mut(|map| map.insert(PLMC_FOREIGN_ID, old_price / 2.into())); +// +// inst.start_auction(project_id, ISSUER_1).unwrap(); +// +// // Increasing the price before the end doesn't make a project under the threshold succeed. +// let evaluations = vec![(EVALUATOR_1, target_evaluation_usd / 2).into()]; +// let evaluation_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// inst.mint_plmc_to(evaluation_plmc); +// +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_2, None); +// inst.evaluate_for_users(project_id, evaluations.clone()).unwrap(); +// +// let old_price = ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap(); +// PRICE_MAP.with_borrow_mut(|map| map.insert(PLMC_FOREIGN_ID, old_price * 2.into())); +// +// let update_block = inst.get_update_block(project_id, &UpdateType::EvaluationEnd).unwrap(); +// let now = inst.current_block(); +// inst.advance_time(update_block - now + 1).unwrap(); +// let project_status = inst.get_project_details(project_id).status; +// assert_eq!(project_status, ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed)); +// } +// +// #[test] +// fn different_decimals_ct_works_as_expected() { +// // Setup some base values to compare different decimals +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let ed = inst.get_ed(); +// let default_project_metadata = default_project_metadata(ISSUER_1); +// let original_decimal_aware_price = default_project_metadata.minimum_price; +// let original_price = ::PriceProvider::convert_back_to_normal_price( +// original_decimal_aware_price, +// USD_DECIMALS, +// default_project_metadata.token_information.decimals, +// ) +// .unwrap(); +// let min_evaluation_amount_usd = ::MinUsdPerEvaluation::get(); +// let stored_plmc_price = +// inst.execute(|| ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap()); +// let usable_plmc_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// PLMC_FOREIGN_ID, +// USD_DECIMALS, +// PLMC_DECIMALS, +// ) +// .unwrap() +// }); +// let min_evaluation_amount_plmc = +// usable_plmc_price.reciprocal().unwrap().checked_mul_int(min_evaluation_amount_usd).unwrap(); +// +// // Test independent of CT decimals - Right PLMC conversion is stored. +// // We move comma 4 places to the left since PLMC has 4 more decimals than USD. +// assert_eq!(stored_plmc_price, FixedU128::from_float(8.4)); +// assert_eq!(usable_plmc_price, FixedU128::from_float(0.00084)); +// +// let mut evaluation_ct_thresholds = Vec::new(); +// let mut evaluation_usd_thresholds = Vec::new(); +// let mut evaluation_plmc_thresholds = Vec::new(); +// +// let mut decimal_test = |decimals: u8| { +// let mut project_metadata = default_project_metadata.clone(); +// project_metadata.token_information.decimals = decimals; +// project_metadata.minimum_price = +// ::PriceProvider::calculate_decimals_aware_price( +// original_price, +// USD_DECIMALS, +// decimals, +// ) +// .unwrap(); +// project_metadata.total_allocation_size = 1_000_000 * 10u128.pow(decimals as u32); +// project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; +// +// let issuer: AccountIdOf = (10_000 + inst.get_new_nonce()).try_into().unwrap(); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); +// +// let evaluation_threshold = inst.execute(|| ::EvaluationSuccessThreshold::get()); +// let evaluation_threshold_ct = evaluation_threshold * project_metadata.total_allocation_size; +// evaluation_ct_thresholds.push(evaluation_threshold_ct); +// +// let evaluation_threshold_usd = +// project_metadata.minimum_price.saturating_mul_int(evaluation_threshold_ct); +// evaluation_usd_thresholds.push(evaluation_threshold_usd); +// +// let evaluation_threshold_plmc = +// usable_plmc_price.reciprocal().unwrap().checked_mul_int(evaluation_threshold_usd).unwrap(); +// evaluation_plmc_thresholds.push(evaluation_threshold_plmc); +// +// // CT price should be multiplied or divided by the amount of decimal difference with USD. +// let decimal_abs_diff = USD_DECIMALS.abs_diff(decimals); +// let original_price_as_usd = original_price.saturating_mul_int(10u128.pow(USD_DECIMALS as u32)); +// let min_price_as_usd = project_metadata.minimum_price.saturating_mul_int(USD_UNIT); +// if decimals < USD_DECIMALS { +// assert_eq!(min_price_as_usd, original_price_as_usd * 10u128.pow(decimal_abs_diff as u32)); +// } else { +// assert_eq!(min_price_as_usd, original_price_as_usd / 10u128.pow(decimal_abs_diff as u32)); +// } +// +// // A minimum evaluation goes through. This is a fixed USD/PLMC value, so independent of CT decimals. +// inst.mint_plmc_to(vec![UserToPLMCBalance::new(EVALUATOR_1, min_evaluation_amount_plmc + ed)]); +// assert_ok!(inst.execute(|| PolimecFunding::evaluate( +// RuntimeOrigin::signed(EVALUATOR_1), +// get_mock_jwt_with_cid( +// EVALUATOR_1, +// InvestorType::Retail, +// generate_did_from_account(EVALUATOR_1), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// min_evaluation_amount_usd +// ))); +// +// // Try bonding up to the threshold with a second evaluation +// inst.mint_plmc_to(vec![UserToPLMCBalance::new( +// EVALUATOR_2, +// evaluation_threshold_plmc + ed - min_evaluation_amount_plmc, +// )]); +// assert_ok!(inst.execute(|| PolimecFunding::evaluate( +// RuntimeOrigin::signed(EVALUATOR_2), +// get_mock_jwt_with_cid( +// EVALUATOR_2, +// InvestorType::Retail, +// generate_did_from_account(EVALUATOR_2), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluation_threshold_usd - min_evaluation_amount_usd +// ))); +// +// // The evaluation should succeed when we bond the threshold PLMC amount in total. +// inst.start_auction(project_id, issuer).unwrap(); +// }; +// +// for decimals in 6..=18 { +// decimal_test(decimals); +// } +// +// // Since we use the same original price and allocation size and adjust for decimals, +// // the USD and PLMC amounts should be the same +// assert!(evaluation_usd_thresholds.iter().all(|x| *x == evaluation_usd_thresholds[0])); +// assert!(evaluation_plmc_thresholds.iter().all(|x| *x == evaluation_plmc_thresholds[0])); +// +// // CT amounts however should be different from each other +// let mut hash_set = HashSet::new(); +// for amount in evaluation_ct_thresholds { +// assert!(!hash_set.contains(&amount)); +// hash_set.insert(amount); +// } +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn round_fails_after_not_enough_bonds() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let now = inst.current_block(); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = default_failing_evaluations(); +// let plmc_eval_deposits: Vec> = +// inst.calculate_evaluation_plmc_spent(evaluations, false); +// let plmc_existential_deposits = plmc_eval_deposits.accounts().existential_deposits(); +// +// let expected_evaluator_balances = inst.generic_map_operation( +// vec![plmc_eval_deposits.clone(), plmc_existential_deposits.clone()], +// MergeOperation::Add, +// ); +// +// inst.mint_plmc_to(plmc_eval_deposits.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// let project_id = inst.create_evaluating_project(project_metadata, issuer, None); +// +// let evaluation_end = inst +// .get_project_details(project_id) +// .round_duration +// .end() +// .expect("Evaluation round end block should be set"); +// +// inst.evaluate_for_users(project_id, default_failing_evaluations()).expect("Bonding should work"); +// +// inst.do_free_plmc_assertions(plmc_existential_deposits); +// inst.do_reserved_plmc_assertions(plmc_eval_deposits, HoldReason::Evaluation(project_id).into()); +// +// inst.advance_time(evaluation_end - now + 1).unwrap(); +// +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// inst.settle_project(project_id).unwrap(); +// inst.do_free_plmc_assertions(expected_evaluator_balances); +// } +// } +// } +// +// #[cfg(test)] +// mod start_evaluation_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn evaluation_starts() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// +// let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); +// let jwt = get_mock_jwt_with_cid( +// issuer, +// InvestorType::Institutional, +// generate_did_from_account(issuer), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); +// assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( +// RuntimeOrigin::signed(issuer), +// jwt, +// project_id +// ))); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationRound); +// } +// +// #[test] +// fn storage_is_updated() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let issuer_did = generate_did_from_account(issuer); +// let project_metadata = default_project_metadata(issuer); +// +// let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); +// let jwt = get_mock_jwt_with_cid( +// issuer, +// InvestorType::Institutional, +// issuer_did.clone(), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let expected_details = ProjectDetailsOf:: { +// issuer_account: ISSUER_1, +// issuer_did, +// is_frozen: true, +// weighted_average_price: None, +// status: ProjectStatus::EvaluationRound, +// round_duration: BlockNumberPair::new(None, None), +// random_end_block: None, +// fundraising_target_usd: project_metadata +// .minimum_price +// .saturating_mul_int(project_metadata.total_allocation_size), +// remaining_contribution_tokens: project_metadata.total_allocation_size, +// funding_amount_reached_usd: 0u128, +// evaluation_round_info: EvaluationRoundInfoOf:: { +// total_bonded_usd: 0u128, +// total_bonded_plmc: 0u128, +// evaluators_outcome: EvaluatorsOutcome::Unchanged, +// }, +// usd_bid_on_oversubscription: None, +// funding_end_block: None, +// migration_type: None, +// }; +// assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( +// RuntimeOrigin::signed(issuer), +// jwt, +// project_id +// ))); +// +// assert_eq!(inst.get_project_details(project_id), expected_details); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// use polimec_common_test_utils::get_mock_jwt; +// +// #[test] +// fn non_institutional_jwt() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// +// let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::start_evaluation( +// RuntimeOrigin::signed(issuer), +// get_mock_jwt_with_cid( +// issuer, +// InvestorType::Professional, +// generate_did_from_account(issuer), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// ), +// Error::::WrongInvestorType +// ); +// }); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::start_evaluation( +// RuntimeOrigin::signed(issuer), +// get_mock_jwt_with_cid( +// issuer, +// InvestorType::Retail, +// generate_did_from_account(issuer), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// ), +// Error::::WrongInvestorType +// ); +// }); +// } +// +// #[test] +// fn evaluation_started_already() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// +// let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); +// let jwt = get_mock_jwt_with_cid( +// issuer, +// InvestorType::Institutional, +// generate_did_from_account(issuer), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); +// assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( +// RuntimeOrigin::signed(issuer), +// jwt.clone(), +// project_id +// ))); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationRound); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::start_evaluation(RuntimeOrigin::signed(issuer), jwt, project_id), +// Error::::IncorrectRound +// ); +// }); +// } +// +// #[test] +// fn no_policy_provided() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let mut project_metadata = default_project_metadata(issuer); +// project_metadata.policy_ipfs_cid = None; +// +// let project_id = inst.create_new_project(project_metadata.clone(), issuer, None); +// let jwt = get_mock_jwt(issuer, InvestorType::Institutional, generate_did_from_account(issuer)); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::start_evaluation(RuntimeOrigin::signed(issuer), jwt, project_id), +// Error::::CidNotProvided +// ); +// }); +// } +// +// #[test] +// fn different_account() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// +// let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1, None); +// let jwt = get_mock_jwt_with_cid( +// ISSUER_1, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); +// assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( +// RuntimeOrigin::signed(ISSUER_1), +// jwt, +// project_id +// ))); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationRound); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::start_evaluation( +// RuntimeOrigin::signed(ISSUER_2), +// get_mock_jwt_with_cid( +// ISSUER_2, +// InvestorType::Institutional, +// generate_did_from_account(ISSUER_2), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// ), +// Error::::NotIssuer +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod evaluate_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use frame_support::traits::fungible::InspectFreeze; +// use pallet_balances::AccountData; +// +// #[test] +// fn all_investor_types() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); +// +// let evaluations = vec![ +// (EVALUATOR_1, 500 * USD_UNIT).into(), +// (EVALUATOR_2, 1000 * USD_UNIT).into(), +// (EVALUATOR_3, 20_000 * USD_UNIT).into(), +// ]; +// let necessary_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// +// inst.mint_plmc_to(necessary_plmc); +// +// assert_ok!(inst.execute(|| PolimecFunding::evaluate( +// RuntimeOrigin::signed(evaluations[0].account), +// get_mock_jwt_with_cid( +// evaluations[0].account, +// InvestorType::Institutional, +// generate_did_from_account(evaluations[0].account), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluations[0].usd_amount, +// ))); +// +// assert_ok!(inst.execute(|| PolimecFunding::evaluate( +// RuntimeOrigin::signed(evaluations[1].account), +// get_mock_jwt_with_cid( +// evaluations[1].account, +// InvestorType::Professional, +// generate_did_from_account(evaluations[1].account), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluations[1].usd_amount, +// ))); +// +// assert_ok!(inst.execute(|| PolimecFunding::evaluate( +// RuntimeOrigin::signed(evaluations[2].account), +// get_mock_jwt_with_cid( +// evaluations[2].account, +// InvestorType::Retail, +// generate_did_from_account(evaluations[2].account), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluations[2].usd_amount, +// ))); +// } +// +// #[test] +// fn using_frozen_tokens() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); +// +// let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * USD_UNIT); +// let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], true); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// +// inst.execute(|| { +// mock::Balances::set_freeze(&(), &EVALUATOR_1, necessary_plmc[0].plmc_amount).unwrap(); +// }); +// +// assert_ok!(inst.execute(|| PolimecFunding::evaluate( +// RuntimeOrigin::signed(evaluation.account), +// get_mock_jwt_with_cid( +// evaluation.account, +// InvestorType::Retail, +// generate_did_from_account(evaluation.account), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluation.usd_amount +// ))); +// } +// +// #[test] +// fn storage_check() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * USD_UNIT); +// let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], false); +// let plmc_existential_deposits = necessary_plmc.accounts().existential_deposits(); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// inst.execute(|| { +// assert_eq!(Evaluations::::iter_values().collect_vec(), vec![]); +// }); +// +// let did = generate_did_from_account(evaluation.account); +// +// assert_ok!(inst.execute(|| PolimecFunding::evaluate( +// RuntimeOrigin::signed(evaluation.account), +// get_mock_jwt_with_cid( +// evaluation.account, +// InvestorType::Retail, +// did.clone(), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluation.usd_amount +// ))); +// +// inst.execute(|| { +// let evaluations = Evaluations::::iter_prefix_values((project_id,)).collect_vec(); +// assert_eq!(evaluations.len(), 1); +// let stored_evaluation = &evaluations[0]; +// let expected_evaluation_item = EvaluationInfoOf:: { +// id: 0, +// did, +// project_id: 0, +// evaluator: EVALUATOR_1, +// original_plmc_bond: necessary_plmc[0].plmc_amount, +// current_plmc_bond: necessary_plmc[0].plmc_amount, +// early_usd_amount: evaluation.usd_amount, +// late_usd_amount: 0, +// when: 1, +// }; +// assert_eq!(stored_evaluation, &expected_evaluation_item); +// }); +// } +// +// #[test] +// fn can_evaluate_with_frozen_tokens() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// +// let evaluation = UserToUSDBalance::new(EVALUATOR_4, 1_000_000 * USD_UNIT); +// let plmc_required = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], false); +// let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); +// let frozen_amount = plmc_required[0].plmc_amount; +// +// inst.mint_plmc_to(plmc_required.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// inst.execute(|| { +// mock::Balances::set_freeze(&(), &EVALUATOR_4, plmc_required[0].plmc_amount).unwrap(); +// }); +// +// inst.execute(|| { +// assert_noop!( +// Balances::transfer_allow_death(RuntimeOrigin::signed(EVALUATOR_4), ISSUER_1, frozen_amount,), +// TokenError::Frozen +// ); +// }); +// +// let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); +// inst.execute(|| { +// assert_ok!(PolimecFunding::evaluate( +// RuntimeOrigin::signed(EVALUATOR_4), +// get_mock_jwt_with_cid( +// EVALUATOR_4, +// InvestorType::Retail, +// generate_did_from_account(EVALUATOR_4), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluation.usd_amount +// )); +// }); +// +// let new_evaluations = default_evaluations(); +// let new_plmc_required = inst.calculate_evaluation_plmc_spent(new_evaluations.clone(), true); +// inst.mint_plmc_to(new_plmc_required.clone()); +// inst.evaluate_for_users(project_id, new_evaluations).unwrap(); +// +// inst.start_auction(project_id, ISSUER_1).unwrap(); +// inst.start_community_funding(project_id).unwrap(); +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// inst.finish_funding(project_id, None).unwrap(); +// +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// +// let free_balance = inst.get_free_plmc_balance_for(EVALUATOR_4); +// let evaluation_held_balance = +// inst.get_reserved_plmc_balance_for(EVALUATOR_4, HoldReason::Evaluation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &EVALUATOR_4)); +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(evaluation_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// +// let treasury_account = ::BlockchainOperationTreasury::get(); +// let pre_slash_treasury_balance = inst.get_free_plmc_balance_for(treasury_account); +// +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// inst.execute(|| { +// PolimecFunding::settle_failed_evaluation( +// RuntimeOrigin::signed(EVALUATOR_4), +// project_id, +// EVALUATOR_4, +// 0, +// ) +// .unwrap(); +// }); +// +// let post_slash_treasury_balance = inst.get_free_plmc_balance_for(treasury_account); +// let free_balance = inst.get_free_plmc_balance_for(EVALUATOR_4); +// let evaluation_held_balance = +// inst.get_reserved_plmc_balance_for(EVALUATOR_4, HoldReason::Evaluation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &EVALUATOR_4)); +// let account_data = inst.execute(|| System::account(&EVALUATOR_4)).data; +// +// let post_slash_evaluation_plmc = +// frozen_amount - (::EvaluatorSlash::get() * frozen_amount); +// assert_eq!(free_balance, inst.get_ed() + post_slash_evaluation_plmc); +// assert_eq!(evaluation_held_balance, Zero::zero()); +// assert_eq!(frozen_balance, frozen_amount); +// let expected_account_data = AccountData { +// free: inst.get_ed() + post_slash_evaluation_plmc, +// reserved: Zero::zero(), +// frozen: frozen_amount, +// flags: Default::default(), +// }; +// assert_eq!(account_data, expected_account_data); +// +// assert!(account_data.frozen > account_data.free); +// assert_eq!( +// post_slash_treasury_balance, +// pre_slash_treasury_balance + ::EvaluatorSlash::get() * frozen_amount +// ); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn project_is_not_in_evaluation_round() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), issuer, None, default_evaluations()); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::evaluate( +// RuntimeOrigin::signed(EVALUATOR_1), +// get_mock_jwt_with_cid( +// EVALUATOR_1, +// InvestorType::Retail, +// generate_did_from_account(EVALUATOR_1), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// 500 * USD_UNIT, +// ), +// Error::::IncorrectRound +// ); +// }); +// } +// +// #[test] +// fn insufficient_plmc_for_desired_evaluation() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = default_evaluations(); +// let insufficient_eval_deposits = inst +// .calculate_evaluation_plmc_spent(evaluations.clone(), false) +// .iter() +// .map(|UserToPLMCBalance { account, plmc_amount }| UserToPLMCBalance::new(*account, plmc_amount / 2)) +// .collect::>>(); +// +// let plmc_existential_deposits = insufficient_eval_deposits.accounts().existential_deposits(); +// +// inst.mint_plmc_to(insufficient_eval_deposits); +// inst.mint_plmc_to(plmc_existential_deposits); +// +// let project_id = inst.create_evaluating_project(project_metadata, issuer, None); +// +// let dispatch_error = inst.evaluate_for_users(project_id, evaluations); +// assert_err!(dispatch_error, TokenError::FundsUnavailable) +// } +// +// #[test] +// fn evaluation_placing_user_balance_under_ed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = vec![UserToUSDBalance::new(EVALUATOR_1, 1000 * USD_UNIT)]; +// let evaluating_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), false); +// let mut plmc_insufficient_existential_deposit = evaluating_plmc.accounts().existential_deposits(); +// +// plmc_insufficient_existential_deposit[0].plmc_amount = +// plmc_insufficient_existential_deposit[0].plmc_amount / 2; +// +// inst.mint_plmc_to(evaluating_plmc); +// inst.mint_plmc_to(plmc_insufficient_existential_deposit); +// +// let project_id = inst.create_evaluating_project(project_metadata, issuer, None); +// +// let dispatch_error = inst.evaluate_for_users(project_id, evaluations); +// assert_err!(dispatch_error, TokenError::FundsUnavailable) +// } +// +// #[test] +// fn cannot_evaluate_more_than_project_limit() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let evaluations = (0u32..::MaxEvaluationsPerProject::get()) +// .map(|i| UserToUSDBalance::::new(i as u32 + 420u32, (100u128 * CT_UNIT).into())) +// .collect_vec(); +// let failing_evaluation = UserToUSDBalance::new(EVALUATOR_1, 1000 * CT_UNIT); +// +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// +// let plmc_for_evaluating = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// +// inst.mint_plmc_to(plmc_for_evaluating.clone()); +// +// inst.evaluate_for_users(project_id, evaluations.clone()).unwrap(); +// +// let plmc_for_failing_evaluating = +// inst.calculate_evaluation_plmc_spent(vec![failing_evaluation.clone()], true); +// +// inst.mint_plmc_to(plmc_for_failing_evaluating.clone()); +// +// assert_err!( +// inst.evaluate_for_users(project_id, vec![failing_evaluation]), +// Error::::TooManyProjectParticipations +// ); +// } +// +// #[test] +// fn cannot_evaluate_more_than_user_limit() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let evaluations = (0u32..::MaxEvaluationsPerUser::get()) +// .map(|_| UserToUSDBalance::::new(EVALUATOR_1, (100u128 * USD_UNIT).into())) +// .collect_vec(); +// let failing_evaluation = UserToUSDBalance::new(EVALUATOR_1, 100 * USD_UNIT); +// +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// +// let plmc_for_evaluating = inst.calculate_evaluation_plmc_spent(evaluations.clone(), false); +// let plmc_existential_deposits = evaluations.accounts().existential_deposits(); +// +// inst.mint_plmc_to(plmc_for_evaluating.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// inst.evaluate_for_users(project_id, evaluations.clone()).unwrap(); +// +// let plmc_for_failing_evaluating = +// inst.calculate_evaluation_plmc_spent(vec![failing_evaluation.clone()], true); +// +// inst.mint_plmc_to(plmc_for_failing_evaluating.clone()); +// +// assert_err!( +// inst.evaluate_for_users(project_id, vec![failing_evaluation]), +// Error::::TooManyUserParticipations +// ); +// } +// +// #[test] +// fn cannot_use_balance_on_hold() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); +// +// let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * USD_UNIT); +// let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], false); +// let ed = necessary_plmc.accounts().existential_deposits(); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// inst.mint_plmc_to(ed.clone()); +// +// inst.execute(|| { +// ::NativeCurrency::hold( +// &RuntimeHoldReason::PolimecFunding(HoldReason::Evaluation(69)), +// &EVALUATOR_1, +// necessary_plmc[0].plmc_amount, +// ) +// .unwrap(); +// }); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::evaluate( +// RuntimeOrigin::signed(evaluation.account), +// get_mock_jwt_with_cid( +// evaluation.account, +// InvestorType::Retail, +// generate_did_from_account(evaluation.account), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluation.usd_amount, +// ), +// TokenError::FundsUnavailable +// ); +// }); +// } +// +// #[test] +// fn issuer_cannot_evaluate_his_project() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// assert_err!( +// inst.execute(|| crate::Pallet::::do_evaluate( +// &(&ISSUER_1 + 1), +// project_id, +// 500 * USD_UNIT, +// generate_did_from_account(ISSUER_1), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )), +// Error::::ParticipationToOwnProject +// ); +// } +// +// #[test] +// fn cannot_use_same_plmc_for_2_evaluations() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); +// +// let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * USD_UNIT); +// let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()], true); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::evaluate( +// RuntimeOrigin::signed(evaluation.account), +// get_mock_jwt_with_cid( +// evaluation.account, +// InvestorType::Retail, +// generate_did_from_account(evaluation.account), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluation.usd_amount, +// )); +// }); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::evaluate( +// RuntimeOrigin::signed(evaluation.account), +// get_mock_jwt_with_cid( +// evaluation.account, +// InvestorType::Retail, +// generate_did_from_account(evaluation.account), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// evaluation.usd_amount, +// ), +// TokenError::FundsUnavailable +// ); +// }); +// } +// +// #[test] +// fn cannot_evaluate_with_less_than_100_usd() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); +// let evaluator = EVALUATOR_1; +// let jwt = get_mock_jwt_with_cid( +// evaluator, +// InvestorType::Retail, +// generate_did_from_account(evaluator), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// +// inst.mint_plmc_to(vec![(evaluator.clone(), 2000 * PLMC).into()]); +// +// // Cannot evaluate with 0 USD +// inst.execute(|| { +// assert_noop!( +// Pallet::::evaluate( +// RuntimeOrigin::signed(evaluator.clone()), +// jwt.clone(), +// project_id, +// 0 +// ), +// Error::::TooLow +// ); +// }); +// +// // Cannot evaluate with less than 99 USD +// inst.execute(|| { +// assert_noop!( +// Pallet::::evaluate( +// RuntimeOrigin::signed(evaluator.clone()), +// jwt.clone(), +// project_id, +// 99 * USD_UNIT +// ), +// Error::::TooLow +// ); +// }); +// } +// +// #[test] +// fn wrong_policy_on_jwt() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::evaluate( +// RuntimeOrigin::signed(EVALUATOR_1), +// get_mock_jwt_with_cid( +// EVALUATOR_1, +// InvestorType::Retail, +// generate_did_from_account(EVALUATOR_1), +// "wrong_cid".as_bytes().to_vec().try_into().unwrap() +// ), +// project_id, +// 500 * USD_UNIT, +// ), +// Error::::PolicyMismatch +// ); +// }); +// } +// } +// } diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index a524a6ca1..ed9133003 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -1,2120 +1,2120 @@ -use super::*; - -#[cfg(test)] -mod round_flow { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use frame_support::traits::fungibles::metadata::Inspect; - use sp_core::bounded_vec; - use std::{collections::HashSet, ops::Not}; - - #[test] - fn auction_round_completed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let evaluations = default_evaluations(); - let bids = default_bids(); - let _project_id = - inst.create_community_contributing_project(project_metadata, ISSUER_1, None, evaluations, bids); - } - - #[test] - fn multiple_auction_projects_completed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project1 = default_project_metadata(ISSUER_1); - let project2 = default_project_metadata(ISSUER_2); - let project3 = default_project_metadata(ISSUER_3); - let project4 = default_project_metadata(ISSUER_4); - let evaluations = default_evaluations(); - let bids = default_bids(); - - inst.create_community_contributing_project(project1, ISSUER_1, None, evaluations.clone(), bids.clone()); - inst.create_community_contributing_project(project2, ISSUER_2, None, evaluations.clone(), bids.clone()); - inst.create_community_contributing_project(project3, ISSUER_3, None, evaluations.clone(), bids.clone()); - inst.create_community_contributing_project(project4, ISSUER_4, None, evaluations, bids); - } - - #[test] - fn wap_is_accurate() { - // From the knowledge hub: https://hub.polimec.org/learn/calculation-example#auction-round-calculation-example - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - const ADAM: u32 = 60; - const TOM: u32 = 61; - const SOFIA: u32 = 62; - const FRED: u32 = 63; - const ANNA: u32 = 64; - const DAMIAN: u32 = 65; - - let accounts = vec![ADAM, TOM, SOFIA, FRED, ANNA, DAMIAN]; - - let bounded_name = bounded_name(); - let bounded_symbol = bounded_symbol(); - let metadata_hash = ipfs_hash(); - let normalized_price = PriceOf::::from_float(10.0); - let decimal_aware_price = PriceProviderOf::::calculate_decimals_aware_price( - normalized_price, - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(); - let project_metadata = ProjectMetadata { - token_information: CurrencyMetadata { - name: bounded_name, - symbol: bounded_symbol, - decimals: CT_DECIMALS, - }, - mainnet_token_max_supply: 8_000_000 * CT_UNIT, - total_allocation_size: 100_000 * CT_UNIT, - auction_round_allocation_percentage: Percent::from_percent(50u8), - minimum_price: decimal_aware_price, - bidding_ticket_sizes: BiddingTicketSizes { - professional: TicketSize::new(5000 * USD_UNIT, None), - institutional: TicketSize::new(5000 * USD_UNIT, None), - phantom: Default::default(), - }, - contributing_ticket_sizes: ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, None), - professional: TicketSize::new(USD_UNIT, None), - institutional: TicketSize::new(USD_UNIT, None), - phantom: Default::default(), - }, - participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), - funding_destination_account: ISSUER_1, - policy_ipfs_cid: Some(metadata_hash), - }; - - // overfund with plmc - let plmc_fundings = accounts - .iter() - .map(|acc| UserToPLMCBalance { account: acc.clone(), plmc_amount: PLMC * 1_000_000 }) - .collect_vec(); - let usdt_fundings = accounts - .iter() - .map(|acc| UserToForeignAssets { - account: acc.clone(), - asset_amount: USD_UNIT * 1_000_000, - asset_id: AcceptedFundingAsset::USDT.to_assethub_id(), - }) - .collect_vec(); - inst.mint_plmc_to(plmc_fundings); - inst.mint_foreign_asset_to(usdt_fundings); - - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); - - let bids = vec![ - (ADAM, 10_000 * CT_UNIT).into(), - (TOM, 20_000 * CT_UNIT).into(), - (SOFIA, 20_000 * CT_UNIT).into(), - (FRED, 10_000 * CT_UNIT).into(), - (ANNA, 5_000 * CT_UNIT).into(), - (DAMIAN, 5_000 * CT_UNIT).into(), - ]; - - inst.bid_for_users(project_id, bids).unwrap(); - - inst.start_community_funding(project_id).unwrap(); - - let token_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let normalized_wap = - PriceProviderOf::::convert_back_to_normal_price(token_price, USD_DECIMALS, CT_DECIMALS) - .unwrap(); - let desired_price = PriceOf::::from_float(11.1818f64); - - assert_close_enough!( - normalized_wap.saturating_mul_int(CT_UNIT), - desired_price.saturating_mul_int(CT_UNIT), - Perquintill::from_float(0.99) - ); - } - - #[test] - fn bids_at_higher_price_than_weighted_average_use_average() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = default_evaluations(); - let mut bids: Vec> = inst.generate_bids_from_total_usd( - project_metadata.minimum_price.saturating_mul_int( - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size, - ), - project_metadata.minimum_price, - default_weights(), - default_bidders(), - default_bidder_multipliers(), - ); - - let second_bucket_bid = (BIDDER_6, 500 * CT_UNIT).into(); - bids.push(second_bucket_bid); - - let project_id = - inst.create_community_contributing_project(project_metadata.clone(), issuer, None, evaluations, bids); - let bidder_5_bid = - inst.execute(|| Bids::::iter_prefix_values((project_id, BIDDER_6)).next().unwrap()); - let wabgp = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let price_normalized = ::PriceProvider::convert_back_to_normal_price( - bidder_5_bid.original_ct_usd_price, - USD_DECIMALS, - project_metadata.token_information.decimals, - ) - .unwrap(); - assert_eq!(price_normalized.to_float(), 11.0); - assert_eq!(bidder_5_bid.final_ct_usd_price, wabgp); - } - - #[test] - fn auction_gets_percentage_of_ct_total_allocation() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let evaluations = default_evaluations(); - let auction_percentage = project_metadata.auction_round_allocation_percentage; - let total_allocation = project_metadata.total_allocation_size; - - let auction_allocation = auction_percentage * total_allocation; - - let bids = vec![(BIDDER_1, auction_allocation).into()]; - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - evaluations.clone(), - bids, - ); - let mut bid_infos = Bids::::iter_prefix_values((project_id,)); - let bid_info = inst.execute(|| bid_infos.next().unwrap()); - assert!(inst.execute(|| bid_infos.next().is_none())); - assert_eq!(bid_info.final_ct_amount, auction_allocation); - - let project_metadata = default_project_metadata(ISSUER_2); - let bids = vec![(BIDDER_1, auction_allocation).into(), (BIDDER_1, 1000 * CT_UNIT).into()]; - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_2, - None, - evaluations.clone(), - bids, - ); - let mut bid_infos = Bids::::iter_prefix_values((project_id,)); - let bid_info_1 = inst.execute(|| bid_infos.next().unwrap()); - let bid_info_2 = inst.execute(|| bid_infos.next().unwrap()); - assert!(inst.execute(|| bid_infos.next().is_none())); - assert_eq!( - bid_info_1.final_ct_amount + bid_info_2.final_ct_amount, - auction_allocation, - "Should not be able to buy more than auction allocation" - ); - } - - // Partial acceptance at price <= wap (refund due to less CT bought) - // Full Acceptance at price > wap (refund due to final price lower than original price paid) - // Rejection due to no more tokens left (full refund) - #[test] - fn bids_get_rejected_and_refunded_part_one() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = default_evaluations(); - - let bid_1 = BidParams::new(BIDDER_1, 5000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let bid_2 = BidParams::new(BIDDER_2, 40_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let bid_3 = BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let bid_4 = BidParams::new(BIDDER_3, 6000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let bid_5 = BidParams::new(BIDDER_4, 2000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - // post bucketing, the bids look like this: - // (BIDDER_1, 5k) - (BIDDER_2, 40k) - (BIDDER_1, 5k) - (BIDDER_1, 5k) - (BIDDER_3 - 5k) - (BIDDER_3 - 1k) - (BIDDER_4 - 2k) - // | -------------------- 1USD ----------------------|---- 1.1 USD ---|---- 1.2 USD ----|----------- 1.3 USD -------------| - // post wap ~ 1.0557252: - // (Accepted, 5k) - (Partially, 32k) - (Rejected, 5k) - (Accepted, 5k) - (Accepted - 5k) - (Accepted - 1k) - (Accepted - 2k) - - let bids = vec![bid_1, bid_2, bid_3, bid_4, bid_5]; - - let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); - - let plmc_fundings = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - false, - ); - let usdt_fundings = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - ); - - let plmc_existential_amounts = plmc_fundings.accounts().existential_deposits(); - - inst.mint_plmc_to(plmc_fundings.clone()); - inst.mint_plmc_to(plmc_existential_amounts.clone()); - inst.mint_foreign_asset_to(usdt_fundings.clone()); - - inst.bid_for_users(project_id, bids.clone()).unwrap(); - - inst.do_free_plmc_assertions(vec![ - UserToPLMCBalance::new(BIDDER_1, inst.get_ed()), - UserToPLMCBalance::new(BIDDER_2, inst.get_ed()), - ]); - inst.do_reserved_plmc_assertions(plmc_fundings.clone(), HoldReason::Participation(project_id).into()); - inst.do_bid_transferred_foreign_asset_assertions(usdt_fundings.clone(), project_id); - - inst.start_community_funding(project_id).unwrap(); - - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let returned_auction_plmc = - inst.calculate_auction_plmc_returned_from_all_bids_made(&bids, project_metadata.clone(), wap); - let returned_funding_assets = - inst.calculate_auction_funding_asset_returned_from_all_bids_made(&bids, project_metadata, wap); - - let expected_free_plmc = inst.generic_map_operation( - vec![returned_auction_plmc.clone(), plmc_existential_amounts], - MergeOperation::Add, - ); - let expected_free_funding_assets = - inst.generic_map_operation(vec![returned_funding_assets.clone()], MergeOperation::Add); - let expected_reserved_plmc = inst - .generic_map_operation(vec![plmc_fundings.clone(), returned_auction_plmc], MergeOperation::Subtract); - let expected_held_funding_assets = inst - .generic_map_operation(vec![usdt_fundings.clone(), returned_funding_assets], MergeOperation::Subtract); - - inst.do_free_plmc_assertions(expected_free_plmc); - - inst.do_reserved_plmc_assertions(expected_reserved_plmc, HoldReason::Participation(project_id).into()); - - inst.do_free_foreign_asset_assertions(expected_free_funding_assets); - inst.do_bid_transferred_foreign_asset_assertions(expected_held_funding_assets, project_id); - } - - #[test] - // Partial acceptance at price > wap (refund due to less CT bought, and final price lower than original price paid) - // Rejection due to bid being made after random end (full refund) - fn bids_get_rejected_and_refunded_part_two() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); - - let total_auction_ct_amount = - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; - - let full_ct_bid_rejected = - BidParams::new(BIDDER_1, total_auction_ct_amount, 1u8, AcceptedFundingAsset::USDT); - let full_ct_bid_partially_accepted = - BidParams::new(BIDDER_2, total_auction_ct_amount, 1u8, AcceptedFundingAsset::USDT); - let oversubscription_bid = BidParams::new(BIDDER_3, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let after_random_end_bid = BidParams::new(BIDDER_4, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - - let all_bids = vec![ - full_ct_bid_rejected.clone(), - full_ct_bid_partially_accepted.clone(), - oversubscription_bid.clone(), - after_random_end_bid.clone(), - ]; - let all_included_bids = - vec![full_ct_bid_rejected.clone(), full_ct_bid_partially_accepted.clone(), oversubscription_bid]; - - let necessary_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &all_bids, - project_metadata.clone(), - None, - false, - ); - let plmc_existential_amounts = necessary_plmc.accounts().existential_deposits(); - let necessary_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &all_bids, - project_metadata.clone(), - None, - ); - - inst.mint_plmc_to(necessary_plmc.clone()); - inst.mint_plmc_to(plmc_existential_amounts.clone()); - inst.mint_foreign_asset_to(necessary_usdt.clone()); - inst.bid_for_users(project_id, all_included_bids.clone()).unwrap(); - inst.advance_time( - ::AuctionOpeningDuration::get() + - ::AuctionClosingDuration::get() - - 1, - ) - .unwrap(); - - inst.bid_for_users(project_id, vec![after_random_end_bid]).unwrap(); - inst.do_free_plmc_assertions(vec![ - UserToPLMCBalance::new(BIDDER_1, inst.get_ed()), - UserToPLMCBalance::new(BIDDER_2, inst.get_ed()), - UserToPLMCBalance::new(BIDDER_3, inst.get_ed()), - UserToPLMCBalance::new(BIDDER_4, inst.get_ed()), - ]); - inst.do_reserved_plmc_assertions(necessary_plmc.clone(), HoldReason::Participation(project_id).into()); - inst.do_bid_transferred_foreign_asset_assertions(necessary_usdt.clone(), project_id); - inst.start_community_funding(project_id).unwrap(); - - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let plmc_returned = inst.calculate_auction_plmc_returned_from_all_bids_made( - &all_included_bids, - project_metadata.clone(), - wap, - ); - let usdt_returned = inst.calculate_auction_funding_asset_returned_from_all_bids_made( - &all_included_bids, - project_metadata.clone(), - wap, - ); - - let rejected_bid_necessary_plmc = &necessary_plmc[3]; - let rejected_bid_necessary_usdt = &necessary_usdt[3]; - - let expected_free = inst.generic_map_operation( - vec![plmc_returned.clone(), plmc_existential_amounts, vec![rejected_bid_necessary_plmc.clone()]], - MergeOperation::Add, - ); - inst.do_free_plmc_assertions(expected_free); - let expected_reserved = inst.generic_map_operation( - vec![necessary_plmc.clone(), plmc_returned.clone(), vec![rejected_bid_necessary_plmc.clone()]], - MergeOperation::Subtract, - ); - inst.do_reserved_plmc_assertions(expected_reserved, HoldReason::Participation(project_id).into()); - let expected_reserved = inst.generic_map_operation( - vec![necessary_usdt.clone(), usdt_returned.clone(), vec![rejected_bid_necessary_usdt.clone()]], - MergeOperation::Subtract, - ); - inst.do_bid_transferred_foreign_asset_assertions(expected_reserved, project_id); - } - - #[test] - fn no_bids_made() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = default_evaluations(); - let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); - - let details = inst.get_project_details(project_id); - let opening_end = details.round_duration.end().unwrap(); - let now = inst.current_block(); - inst.advance_time(opening_end - now + 2).unwrap(); - - let details = inst.get_project_details(project_id); - let closing_end = details.round_duration.end().unwrap(); - let now = inst.current_block(); - inst.advance_time(closing_end - now + 2).unwrap(); - - let details = inst.get_project_details(project_id); - assert!(matches!(details.status, ProjectStatus::CommunityRound(..))); - assert_eq!(details.weighted_average_price, Some(project_metadata.minimum_price)); - } - - #[test] - fn all_bids_rejected() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = default_evaluations(); - let bids = default_bids(); - let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); - - let necessary_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - true, - ); - let necessary_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - ); - - inst.mint_plmc_to(necessary_plmc.clone()); - inst.mint_foreign_asset_to(necessary_usdt.clone()); - inst.advance_time( - ::AuctionOpeningDuration::get() + - ::AuctionClosingDuration::get() - - 1, - ) - .unwrap(); - - // We bid at the last block, which we assume will be after the random end - inst.bid_for_users(project_id, bids.clone()).unwrap(); - - inst.start_community_funding(project_id).unwrap(); - - let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); - // let non_rejected_bids = stored_bids - // .into_iter() - // .filter(|bid| { - // (bid.final_ct_amount == 0 && bid.status == BidStatus::Rejected(RejectionReason::RejectionReason)) - // .not() - // }) - // .count(); - // assert_eq!(non_rejected_bids, 0); - // assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound); - } - - #[test] - fn wap_from_different_funding_assets() { - // From the knowledge hub: https://hub.polimec.org/learn/calculation-example#auction-round-calculation-example - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - const ADAM: u32 = 60; - const TOM: u32 = 61; - const SOFIA: u32 = 62; - const FRED: u32 = 63; - const ANNA: u32 = 64; - const DAMIAN: u32 = 65; - - let accounts = vec![ADAM, TOM, SOFIA, FRED, ANNA, DAMIAN]; - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.total_allocation_size = 100_000 * CT_UNIT; - project_metadata.participation_currencies = - bounded_vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT,]; - - // overfund with plmc - let plmc_fundings = accounts - .iter() - .map(|acc| UserToPLMCBalance { account: acc.clone(), plmc_amount: PLMC * 1_000_000 }) - .collect_vec(); - - let fundings = [AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT]; - assert_eq!(fundings.len(), AcceptedFundingAsset::VARIANT_COUNT); - let mut fundings = fundings.into_iter().cycle(); - - let usdt_fundings = accounts - .iter() - .map(|acc| { - let accepted_asset = fundings.next().unwrap(); - let asset_id = accepted_asset.to_assethub_id(); - let asset_decimals = inst.execute(|| ::FundingCurrency::decimals(asset_id)); - let asset_unit = 10u128.checked_pow(asset_decimals.into()).unwrap(); - UserToForeignAssets { account: acc.clone(), asset_amount: asset_unit * 1_000_000, asset_id } - }) - .collect_vec(); - inst.mint_plmc_to(plmc_fundings); - inst.mint_foreign_asset_to(usdt_fundings); - - let project_id = inst.create_auctioning_project(project_metadata, ISSUER_1, None, default_evaluations()); - - let bids = vec![ - (ADAM, 10_000 * CT_UNIT, 1, AcceptedFundingAsset::USDT).into(), - (TOM, 20_000 * CT_UNIT, 1, AcceptedFundingAsset::USDC).into(), - (SOFIA, 20_000 * CT_UNIT, 1, AcceptedFundingAsset::DOT).into(), - (FRED, 10_000 * CT_UNIT, 1, AcceptedFundingAsset::USDT).into(), - (ANNA, 5_000 * CT_UNIT, 1, AcceptedFundingAsset::USDC).into(), - (DAMIAN, 5_000 * CT_UNIT, 1, AcceptedFundingAsset::DOT).into(), - ]; - - inst.bid_for_users(project_id, bids).unwrap(); - - inst.start_community_funding(project_id).unwrap(); - - let token_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let normalized_wap = - PriceProviderOf::::convert_back_to_normal_price(token_price, USD_DECIMALS, CT_DECIMALS) - .unwrap(); - - let desired_price = PriceOf::::from_float(11.1818f64); - - assert_close_enough!( - normalized_wap.saturating_mul_int(USD_UNIT), - desired_price.saturating_mul_int(USD_UNIT), - Perquintill::from_float(0.99) - ); - } - - #[test] - fn different_decimals_ct_works_as_expected() { - // Setup some base values to compare different decimals - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let ed = inst.get_ed(); - let default_project_metadata = default_project_metadata(ISSUER_1); - let original_decimal_aware_price = default_project_metadata.minimum_price; - let original_price = ::PriceProvider::convert_back_to_normal_price( - original_decimal_aware_price, - USD_DECIMALS, - default_project_metadata.token_information.decimals, - ) - .unwrap(); - let usable_plmc_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - PLMC_FOREIGN_ID, - USD_DECIMALS, - PLMC_DECIMALS, - ) - .unwrap() - }); - let usdt_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::USDT.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::USDT.to_assethub_id()), - ) - .unwrap() - }); - let usdc_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::USDC.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::USDC.to_assethub_id()), - ) - .unwrap() - }); - let dot_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::DOT.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::DOT.to_assethub_id()), - ) - .unwrap() - }); - - let mut funding_assets_cycle = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] - .into_iter() - .cycle(); - - let mut min_bid_amounts_ct = Vec::new(); - let mut min_bid_amounts_usd = Vec::new(); - let mut auction_allocations_ct = Vec::new(); - let mut auction_allocations_usd = Vec::new(); - - let mut decimal_test = |decimals: u8| { - let funding_asset = funding_assets_cycle.next().unwrap(); - let funding_asset_usd_price = match funding_asset { - AcceptedFundingAsset::USDT => usdt_price, - AcceptedFundingAsset::USDC => usdc_price, - AcceptedFundingAsset::DOT => dot_price, - }; - - let mut project_metadata = default_project_metadata.clone(); - project_metadata.token_information.decimals = decimals; - project_metadata.minimum_price = - ::PriceProvider::calculate_decimals_aware_price( - original_price, - USD_DECIMALS, - decimals, - ) - .unwrap(); - - project_metadata.total_allocation_size = 1_000_000 * 10u128.pow(decimals as u32); - project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; - project_metadata.participation_currencies = bounded_vec!(funding_asset); - - let issuer: AccountIdOf = (10_000 + inst.get_new_nonce()).try_into().unwrap(); - let evaluations = inst.generate_successful_evaluations( - project_metadata.clone(), - default_evaluators(), - default_weights(), - ); - let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); - - let auction_allocation_percentage = project_metadata.auction_round_allocation_percentage; - let auction_allocation_ct = auction_allocation_percentage * project_metadata.total_allocation_size; - auction_allocations_ct.push(auction_allocation_ct); - let auction_allocation_usd = project_metadata.minimum_price.saturating_mul_int(auction_allocation_ct); - auction_allocations_usd.push(auction_allocation_usd); - - let min_professional_bid_usd = - project_metadata.bidding_ticket_sizes.professional.usd_minimum_per_participation; - min_bid_amounts_usd.push(min_professional_bid_usd); - let min_professional_bid_ct = - project_metadata.minimum_price.reciprocal().unwrap().saturating_mul_int(min_professional_bid_usd); - let min_professional_bid_plmc = - usable_plmc_price.reciprocal().unwrap().saturating_mul_int(min_professional_bid_usd); - min_bid_amounts_ct.push(min_professional_bid_ct); - let min_professional_bid_funding_asset = - funding_asset_usd_price.reciprocal().unwrap().saturating_mul_int(min_professional_bid_usd); - - // Every project should want to raise 5MM USD on the auction round regardless of CT decimals - assert_eq!(auction_allocation_usd, 5_000_000 * USD_UNIT); - - // A minimum bid goes through. This is a fixed USD value, but the extrinsic amount depends on CT decimals. - inst.mint_plmc_to(vec![UserToPLMCBalance::new(BIDDER_1, min_professional_bid_plmc + ed)]); - inst.mint_foreign_asset_to(vec![UserToForeignAssets::new( - BIDDER_1, - min_professional_bid_funding_asset, - funding_asset.to_assethub_id(), - )]); - - assert_ok!(inst.execute(|| PolimecFunding::bid( - RuntimeOrigin::signed(BIDDER_1), - get_mock_jwt_with_cid( - BIDDER_1, - InvestorType::Professional, - generate_did_from_account(BIDDER_1), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - min_professional_bid_ct, - 1u8.try_into().unwrap(), - funding_asset, - ))); - - // The bucket should have 50% of 1MM * 10^decimals CT minus what we just bid - let bucket = inst.execute(|| Buckets::::get(project_id).unwrap()); - assert_eq!(bucket.amount_left, 500_000u128 * 10u128.pow(decimals as u32) - min_professional_bid_ct); - }; - - for decimals in 6..=18 { - decimal_test(decimals); - } - - // Since we use the same original price and allocation size and adjust for decimals, - // the USD amounts should be the same - assert!(min_bid_amounts_usd.iter().all(|x| *x == min_bid_amounts_usd[0])); - assert!(auction_allocations_usd.iter().all(|x| *x == auction_allocations_usd[0])); - - // CT amounts however should be different from each other - let mut hash_set_1 = HashSet::new(); - for amount in min_bid_amounts_ct { - assert!(!hash_set_1.contains(&amount)); - hash_set_1.insert(amount); - } - let mut hash_set_2 = HashSet::new(); - for amount in auction_allocations_ct { - assert!(!hash_set_2.contains(&amount)); - hash_set_2.insert(amount); - } - } - - #[test] - fn all_bids_but_one_have_price_higher_than_wap() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let total_allocation = 10_000_000 * CT_UNIT; - let min_bid_ct = 500 * CT_UNIT; // 5k USD at 10USD/CT - let max_bids_per_project: u32 = ::MaxBidsPerProject::get(); - let big_bid: BidParams = (BIDDER_1, total_allocation).into(); - let small_bids: Vec> = - (0..max_bids_per_project - 1).map(|i| (i + BIDDER_1, min_bid_ct).into()).collect(); - let all_bids = vec![vec![big_bid.clone()], small_bids.clone()].into_iter().flatten().collect_vec(); - - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.mainnet_token_max_supply = total_allocation; - project_metadata.total_allocation_size = total_allocation; - project_metadata.auction_round_allocation_percentage = Percent::from_percent(100); - - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()), - all_bids, - ); - - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let all_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); - - let higher_than_wap_bids = all_bids.iter().filter(|bid| bid.original_ct_usd_price > wap).collect_vec(); - assert_eq!(higher_than_wap_bids.len(), (max_bids_per_project - 1u32) as usize); - } - - #[test] - fn auction_oversubscription() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let auction_allocation = - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; - let bucket_size = Percent::from_percent(10) * auction_allocation; - let bids = vec![ - (BIDDER_1, auction_allocation).into(), - (BIDDER_2, bucket_size).into(), - (BIDDER_3, bucket_size).into(), - (BIDDER_4, bucket_size).into(), - (BIDDER_5, bucket_size).into(), - (BIDDER_6, bucket_size).into(), - ]; - - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - bids, - ); - - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - dbg!(wap); - assert!(wap > project_metadata.minimum_price); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn contribute_does_not_work() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - let did = generate_did_from_account(ISSUER_1); - let investor_type = InvestorType::Retail; - inst.execute(|| { - assert_noop!( - PolimecFunding::do_contribute( - &BIDDER_1, - project_id, - 100, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - did, - investor_type, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - Error::::IncorrectRound - ); - }); - } - } -} - -#[cfg(test)] -mod start_auction_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn pallet_can_start_auction_automatically() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); - let evaluations = default_evaluations(); - let required_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - - inst.mint_plmc_to(required_plmc); - inst.evaluate_for_users(project_id, evaluations).unwrap(); - - let update_block = inst.get_update_block(project_id, &UpdateType::EvaluationEnd).unwrap(); - inst.execute(|| System::set_block_number(update_block - 1)); - inst.advance_time(1).unwrap(); - - let update_block = inst.get_update_block(project_id, &UpdateType::AuctionOpeningStart).unwrap(); - inst.execute(|| System::set_block_number(update_block - 1)); - inst.advance_time(1).unwrap(); - } - - #[test] - fn issuer_can_start_auction_manually() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); - let evaluations = default_evaluations(); - let required_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - inst.mint_plmc_to(required_plmc); - inst.evaluate_for_users(project_id, evaluations).unwrap(); - inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionInitializePeriod); - inst.advance_time(1).unwrap(); - inst.execute(|| Pallet::::do_start_auction(ISSUER_1, project_id)).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Auction); - } - - #[test] - fn stranger_cannot_start_auction_manually() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); - let evaluations = default_evaluations(); - let required_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - inst.mint_plmc_to(required_plmc); - inst.evaluate_for_users(project_id, evaluations).unwrap(); - inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionInitializePeriod); - inst.advance_time(1).unwrap(); - - for account in 6000..6010 { - inst.execute(|| { - let response = Pallet::::do_start_auction(account, project_id); - assert_noop!(response, Error::::NotIssuer); - }); - } - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn cannot_start_auction_manually_before_evaluation_finishes() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); - inst.execute(|| { - assert_noop!( - PolimecFunding::do_start_auction(ISSUER_1, project_id), - Error::::TransitionPointNotSet - ); - }); - } - - #[test] - fn cannot_start_auction_manually_if_evaluation_fails() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); - inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); - inst.execute(|| { - assert_noop!( - PolimecFunding::do_start_auction(ISSUER_1, project_id), - Error::::TransitionPointNotSet - ); - }); - assert_eq!( - inst.get_project_details(project_id).status, - ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) - ); - } - - #[test] - fn auction_doesnt_start_automatically_if_evaluation_fails() { - // Test our success assumption is ok - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); - let evaluations = default_evaluations(); - let required_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - inst.mint_plmc_to(required_plmc); - inst.evaluate_for_users(project_id, evaluations).unwrap(); - inst.start_auction(project_id, ISSUER_1).unwrap(); - - // Main test with failed evaluation - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); - - let evaluation_end_execution = inst.get_update_block(project_id, &UpdateType::EvaluationEnd).unwrap(); - inst.execute(|| System::set_block_number(evaluation_end_execution - 1)); - inst.advance_time(1).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - } - } -} - -#[cfg(test)] -mod bid_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use frame_support::{dispatch::DispatchResultWithPostInfo, traits::fungible::InspectFreeze}; - - #[test] - fn evaluation_bond_counts_towards_bid() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let mut evaluations = default_evaluations(); - let evaluator_bidder = 69; - let evaluation_amount = 420 * USD_UNIT; - let evaluator_bid = BidParams::new(evaluator_bidder, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - evaluations.push((evaluator_bidder, evaluation_amount).into()); - - let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); - - let already_bonded_plmc = inst - .calculate_evaluation_plmc_spent(vec![(evaluator_bidder, evaluation_amount).into()], false)[0] - .plmc_amount; - - let usable_evaluation_plmc = - already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; - - let necessary_plmc_for_bid = inst.calculate_auction_plmc_charged_with_given_price( - &vec![evaluator_bid.clone()], - project_metadata.minimum_price, - false, - )[0] - .plmc_amount; - - let necessary_usdt_for_bid = inst.calculate_auction_funding_asset_charged_with_given_price( - &vec![evaluator_bid.clone()], - project_metadata.minimum_price, - ); - - inst.mint_plmc_to(vec![UserToPLMCBalance::new( - evaluator_bidder, - necessary_plmc_for_bid - usable_evaluation_plmc, - )]); - inst.mint_foreign_asset_to(necessary_usdt_for_bid); - - inst.bid_for_users(project_id, vec![evaluator_bid]).unwrap(); - - let evaluation_items = inst.execute(|| { - Evaluations::::iter_prefix_values((project_id, evaluator_bidder)).collect_vec() - }); - assert_eq!(evaluation_items.len(), 1); - assert_eq!(evaluation_items[0].current_plmc_bond, already_bonded_plmc - usable_evaluation_plmc); - - let bid_items = - inst.execute(|| Bids::::iter_prefix_values((project_id, evaluator_bidder)).collect_vec()); - assert_eq!(bid_items.len(), 1); - assert_eq!(bid_items[0].plmc_bond, necessary_plmc_for_bid); - - inst.do_reserved_plmc_assertions( - vec![UserToPLMCBalance::new(evaluator_bidder, necessary_plmc_for_bid)], - HoldReason::Participation(project_id).into(), - ); - inst.do_reserved_plmc_assertions( - vec![UserToPLMCBalance::new(evaluator_bidder, already_bonded_plmc - usable_evaluation_plmc)], - HoldReason::Evaluation(project_id).into(), - ); - } - - #[test] - fn bid_with_multiple_currencies() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let mut project_metadata_all = default_project_metadata(ISSUER_1); - project_metadata_all.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] - .try_into() - .unwrap(); - - let mut project_metadata_usdt = default_project_metadata(ISSUER_2); - project_metadata_usdt.participation_currencies = vec![AcceptedFundingAsset::USDT].try_into().unwrap(); - - let mut project_metadata_usdc = default_project_metadata(ISSUER_3); - project_metadata_usdc.participation_currencies = vec![AcceptedFundingAsset::USDC].try_into().unwrap(); - - let mut project_metadata_dot = default_project_metadata(ISSUER_4); - project_metadata_dot.participation_currencies = vec![AcceptedFundingAsset::DOT].try_into().unwrap(); - - let evaluations = default_evaluations(); - - let usdt_bid = BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let usdc_bid = BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); - let dot_bid = BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); - - let plmc_fundings = inst.calculate_auction_plmc_charged_with_given_price( - &vec![usdt_bid.clone(), usdc_bid.clone(), dot_bid.clone()], - project_metadata_all.minimum_price, - true, - ); - - inst.mint_plmc_to(plmc_fundings.clone()); - inst.mint_plmc_to(plmc_fundings.clone()); - inst.mint_plmc_to(plmc_fundings.clone()); - - let usdt_fundings = inst.calculate_auction_funding_asset_charged_with_given_price( - &vec![usdt_bid.clone(), usdc_bid.clone(), dot_bid.clone()], - project_metadata_all.minimum_price, - ); - inst.mint_foreign_asset_to(usdt_fundings.clone()); - inst.mint_foreign_asset_to(usdt_fundings.clone()); - inst.mint_foreign_asset_to(usdt_fundings.clone()); - - let project_id_all = - inst.create_auctioning_project(project_metadata_all, ISSUER_1, None, evaluations.clone()); - assert_ok!(inst.bid_for_users(project_id_all, vec![usdt_bid.clone(), usdc_bid.clone(), dot_bid.clone()])); - - let project_id_usdt = - inst.create_auctioning_project(project_metadata_usdt, ISSUER_2, None, evaluations.clone()); - assert_ok!(inst.bid_for_users(project_id_usdt, vec![usdt_bid.clone()])); - assert_err!( - inst.bid_for_users(project_id_usdt, vec![usdc_bid.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_err!( - inst.bid_for_users(project_id_usdt, vec![dot_bid.clone()]), - Error::::FundingAssetNotAccepted - ); - - let project_id_usdc = - inst.create_auctioning_project(project_metadata_usdc, ISSUER_3, None, evaluations.clone()); - assert_err!( - inst.bid_for_users(project_id_usdc, vec![usdt_bid.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_ok!(inst.bid_for_users(project_id_usdc, vec![usdc_bid.clone()])); - assert_err!( - inst.bid_for_users(project_id_usdc, vec![dot_bid.clone()]), - Error::::FundingAssetNotAccepted - ); - - let project_id_dot = - inst.create_auctioning_project(project_metadata_dot, ISSUER_4, None, evaluations.clone()); - assert_err!( - inst.bid_for_users(project_id_dot, vec![usdt_bid.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_err!( - inst.bid_for_users(project_id_dot, vec![usdc_bid.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_ok!(inst.bid_for_users(project_id_dot, vec![dot_bid.clone()])); - } - - fn test_bid_setup( - inst: &mut MockInstantiator, - project_id: ProjectId, - bidder: AccountIdOf, - investor_type: InvestorType, - u8_multiplier: u8, - ) -> DispatchResultWithPostInfo { - let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); - let jwt = get_mock_jwt_with_cid( - bidder.clone(), - investor_type, - generate_did_from_account(BIDDER_1), - project_policy, - ); - let amount = 1000 * CT_UNIT; - let multiplier = Multiplier::force_new(u8_multiplier); - - if u8_multiplier > 0 { - let bid = BidParams:: { - bidder: bidder.clone(), - amount, - multiplier, - asset: AcceptedFundingAsset::USDT, - }; - let min_price = inst.get_project_metadata(project_id).minimum_price; - let necessary_plmc = - inst.calculate_auction_plmc_charged_with_given_price(&vec![bid.clone()], min_price, true); - let necessary_usdt = - inst.calculate_auction_funding_asset_charged_with_given_price(&vec![bid.clone()], min_price); - - inst.mint_plmc_to(necessary_plmc.clone()); - inst.mint_foreign_asset_to(necessary_usdt.clone()); - } - inst.execute(|| { - Pallet::::bid( - RuntimeOrigin::signed(bidder), - jwt, - project_id, - amount, - multiplier, - AcceptedFundingAsset::USDT, - ) - }) - } - - #[test] - fn multiplier_limits() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); - let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations); - // Professional bids: 0x multiplier should fail - assert_err!( - test_bid_setup(&mut inst, project_id, BIDDER_1, InvestorType::Professional, 0), - Error::::ForbiddenMultiplier - ); - // Professional bids: 1 - 10x multiplier should work - for multiplier in 1..=10u8 { - assert_ok!(test_bid_setup(&mut inst, project_id, BIDDER_1, InvestorType::Professional, multiplier)); - } - // Professional bids: >=11x multiplier should fail - for multiplier in 11..=50u8 { - assert_err!( - test_bid_setup(&mut inst, project_id, BIDDER_1, InvestorType::Professional, multiplier), - Error::::ForbiddenMultiplier - ); - } - - // Institutional bids: 0x multiplier should fail - assert_err!( - test_bid_setup(&mut inst, project_id, BIDDER_2, InvestorType::Institutional, 0), - Error::::ForbiddenMultiplier - ); - // Institutional bids: 1 - 25x multiplier should work - for multiplier in 1..=25u8 { - assert_ok!(test_bid_setup(&mut inst, project_id, BIDDER_2, InvestorType::Institutional, multiplier)); - } - // Institutional bids: >=26x multiplier should fail - for multiplier in 26..=50u8 { - assert_err!( - test_bid_setup(&mut inst, project_id, BIDDER_2, InvestorType::Institutional, multiplier), - Error::::ForbiddenMultiplier - ); - } - } - - #[test] - fn bid_split_into_multiple_buckets() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(1.0), - USD_DECIMALS, - project_metadata.clone().token_information.decimals, - ) - .unwrap(); - project_metadata.auction_round_allocation_percentage = Percent::from_percent(50u8); - - let evaluations = default_evaluations(); - let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations); - - // bid that fills 80% of the first bucket - let bid_40_percent = inst.generate_bids_from_total_ct_percent( - project_metadata.clone(), - 40u8, - vec![100], - vec![BIDDER_1], - vec![8u8], - ); - - // Note: 5% of total CTs is one bucket, i.e 10% of the auction allocation - // This bid fills last 20% of the first bucket, - // and gets split into 3 more bids of 2 more full and one partially full buckets. - // 10% + 5% + 5% + 3% = 23% - let bid_23_percent = inst.generate_bids_from_total_ct_percent( - project_metadata.clone(), - 23u8, - vec![100], - vec![BIDDER_2], - vec![7u8], - ); - - let all_bids = vec![bid_40_percent[0].clone(), bid_23_percent[0].clone()]; - - let necessary_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &all_bids, - project_metadata.clone(), - None, - true, - ); - let necessary_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &all_bids, - project_metadata.clone(), - None, - ); - inst.mint_plmc_to(necessary_plmc.clone()); - inst.mint_foreign_asset_to(necessary_usdt.clone()); - - inst.bid_for_users(project_id, bid_40_percent.clone()).unwrap(); - let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); - assert_eq!(stored_bids.len(), 1); - - inst.bid_for_users(project_id, bid_23_percent.clone()).unwrap(); - let mut stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); - stored_bids.sort_by(|a, b| a.id.cmp(&b.id)); - // 40% + 10% + 5% + 5% + 3% = 5 total bids - assert_eq!(stored_bids.len(), 5); - - let normalize_price = |decimal_aware_price| { - PriceProviderOf::::convert_back_to_normal_price( - decimal_aware_price, - USD_DECIMALS, - project_metadata.clone().token_information.decimals, - ) - .unwrap() - }; - assert_eq!(normalize_price(stored_bids[1].original_ct_usd_price), PriceOf::::from_float(1.0)); - assert_eq!( - stored_bids[1].original_ct_amount, - Percent::from_percent(10) * project_metadata.total_allocation_size - ); - assert_eq!( - normalize_price(stored_bids[2].original_ct_usd_price), - PriceOf::::from_rational(11, 10) - ); - assert_eq!( - stored_bids[2].original_ct_amount, - Percent::from_percent(5) * project_metadata.total_allocation_size - ); - - assert_eq!(normalize_price(stored_bids[3].original_ct_usd_price), PriceOf::::from_float(1.2)); - assert_eq!( - stored_bids[3].original_ct_amount, - Percent::from_percent(5) * project_metadata.total_allocation_size - ); - - assert_eq!(normalize_price(stored_bids[4].original_ct_usd_price), PriceOf::::from_float(1.3)); - assert_eq!( - stored_bids[4].original_ct_amount, - Percent::from_percent(3) * project_metadata.total_allocation_size - ); - let current_bucket = inst.execute(|| Buckets::::get(project_id)).unwrap(); - assert_eq!(normalize_price(current_bucket.current_price), PriceOf::::from_float(1.3)); - assert_eq!(current_bucket.amount_left, Percent::from_percent(2) * project_metadata.total_allocation_size); - assert_eq!(normalize_price(current_bucket.delta_price), PriceOf::::from_float(0.1)); - } - - #[test] - fn can_bid_with_frozen_tokens_funding_failed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = - inst.create_auctioning_project(project_metadata.clone(), issuer, None, default_evaluations()); - - let bid = BidParams::new(BIDDER_4, 500 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let plmc_required = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &vec![bid.clone()], - project_metadata.clone(), - None, - false, - ); - let frozen_amount = plmc_required[0].plmc_amount; - let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); - - inst.mint_plmc_to(plmc_existential_deposits); - inst.mint_plmc_to(plmc_required.clone()); - - inst.execute(|| { - mock::Balances::set_freeze(&(), &BIDDER_4, plmc_required[0].plmc_amount).unwrap(); - }); - - let usdt_required = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &vec![bid.clone()], - project_metadata.clone(), - None, - ); - inst.mint_foreign_asset_to(usdt_required); - - inst.execute(|| { - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(BIDDER_4), ISSUER_1, frozen_amount,), - TokenError::Frozen - ); - }); - - inst.execute(|| { - assert_ok!(PolimecFunding::bid( - RuntimeOrigin::signed(BIDDER_4), - get_mock_jwt_with_cid( - BIDDER_4, - InvestorType::Institutional, - generate_did_from_account(BIDDER_4), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - bid.amount, - bid.multiplier, - bid.asset - )); - }); - - inst.start_community_funding(project_id).unwrap(); - inst.start_remainder_or_end_funding(project_id).unwrap(); - inst.finish_funding(project_id, None).unwrap(); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - - let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - inst.execute(|| { - PolimecFunding::settle_failed_bid(RuntimeOrigin::signed(BIDDER_4), project_id, BIDDER_4, 0).unwrap(); - }); - - let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Evaluation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); - - assert_eq!(free_balance, inst.get_ed() + frozen_amount); - assert_eq!(bid_held_balance, Zero::zero()); - assert_eq!(frozen_balance, frozen_amount); - } - - #[test] - fn can_bid_with_frozen_tokens_funding_success() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = - inst.create_auctioning_project(project_metadata.clone(), issuer, None, default_evaluations()); - - let bid = BidParams::new(BIDDER_4, 500 * CT_UNIT, 5u8, AcceptedFundingAsset::USDT); - let plmc_required = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &vec![bid.clone()], - project_metadata.clone(), - None, - false, - ); - let frozen_amount = plmc_required[0].plmc_amount; - let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); - - inst.mint_plmc_to(plmc_existential_deposits); - inst.mint_plmc_to(plmc_required.clone()); - - inst.execute(|| { - mock::Balances::set_freeze(&(), &BIDDER_4, plmc_required[0].plmc_amount).unwrap(); - }); - - let usdt_required = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &vec![bid.clone()], - project_metadata.clone(), - None, - ); - inst.mint_foreign_asset_to(usdt_required); - - inst.execute(|| { - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(BIDDER_4), ISSUER_1, frozen_amount,), - TokenError::Frozen - ); - }); - - inst.execute(|| { - assert_ok!(PolimecFunding::bid( - RuntimeOrigin::signed(BIDDER_4), - get_mock_jwt_with_cid( - BIDDER_4, - InvestorType::Institutional, - generate_did_from_account(BIDDER_4), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - bid.amount, - bid.multiplier, - bid.asset - )); - }); - - inst.start_community_funding(project_id).unwrap(); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let contributions = inst.generate_contributions_from_total_ct_percent( - project_metadata.clone(), - 90u8, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let plmc_required = inst.calculate_contributed_plmc_spent(contributions.clone(), wap, false); - let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_required.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - let usdt_required = inst.calculate_contributed_funding_asset_spent(contributions.clone(), wap); - inst.mint_foreign_asset_to(usdt_required.clone()); - - inst.contribute_for_users(project_id, contributions).unwrap(); - - inst.start_remainder_or_end_funding(project_id).unwrap(); - inst.finish_funding(project_id, None).unwrap(); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - - inst.execute(|| { - PolimecFunding::settle_successful_bid(RuntimeOrigin::signed(BIDDER_4), project_id, BIDDER_4, 0) - .unwrap(); - }); - - let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - - let vest_duration = - MultiplierOf::::new(5u8).unwrap().calculate_vesting_duration::(); - let now = inst.current_block(); - inst.jump_to_block(now + vest_duration + 1u64); - inst.execute(|| { - assert_ok!(mock::LinearRelease::vest( - RuntimeOrigin::signed(BIDDER_4), - HoldReason::Participation(project_id).into() - )); - }); - - let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); - - assert_eq!(free_balance, inst.get_ed() + frozen_amount); - assert_eq!(bid_held_balance, Zero::zero()); - assert_eq!(frozen_balance, frozen_amount); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn cannot_use_all_of_evaluation_bond_on_bid() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let mut evaluations = default_evaluations(); - let evaluator_bidder = 69; - let evaluation_amount = 420 * USD_UNIT; - let evaluator_bid = BidParams::new(evaluator_bidder, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - evaluations.push((evaluator_bidder, evaluation_amount).into()); - - let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); - - let necessary_usdt_for_bid = inst.calculate_auction_funding_asset_charged_with_given_price( - &vec![evaluator_bid.clone()], - project_metadata.minimum_price, - ); - - inst.mint_foreign_asset_to(necessary_usdt_for_bid); - - assert_err!( - inst.bid_for_users(project_id, vec![evaluator_bid]), - Error::::ParticipantNotEnoughFunds - ); - } - - #[test] - fn cannot_use_evaluation_bond_on_another_project_bid() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata_1 = default_project_metadata(ISSUER_1); - let project_metadata_2 = default_project_metadata(ISSUER_2); - - let mut evaluations_1 = default_evaluations(); - let evaluations_2 = default_evaluations(); - - let evaluator_bidder = 69; - let evaluation_amount = 420 * USD_UNIT; - let evaluator_bid = BidParams::new(evaluator_bidder, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - evaluations_1.push((evaluator_bidder, evaluation_amount).into()); - - let _project_id_1 = - inst.create_auctioning_project(project_metadata_1.clone(), ISSUER_1, None, evaluations_1); - let project_id_2 = - inst.create_auctioning_project(project_metadata_2.clone(), ISSUER_2, None, evaluations_2); - - // Necessary Mints - let already_bonded_plmc = inst - .calculate_evaluation_plmc_spent(vec![(evaluator_bidder, evaluation_amount).into()], false)[0] - .plmc_amount; - let usable_evaluation_plmc = - already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; - let necessary_plmc_for_bid = inst.calculate_auction_plmc_charged_with_given_price( - &vec![evaluator_bid.clone()], - project_metadata_2.minimum_price, - false, - )[0] - .plmc_amount; - let necessary_usdt_for_bid = inst.calculate_auction_funding_asset_charged_with_given_price( - &vec![evaluator_bid.clone()], - project_metadata_2.minimum_price, - ); - inst.mint_plmc_to(vec![UserToPLMCBalance::new( - evaluator_bidder, - necessary_plmc_for_bid - usable_evaluation_plmc, - )]); - inst.mint_foreign_asset_to(necessary_usdt_for_bid); - - inst.execute(|| { - assert_noop!( - PolimecFunding::bid( - RuntimeOrigin::signed(evaluator_bidder), - get_mock_jwt_with_cid( - evaluator_bidder, - InvestorType::Professional, - generate_did_from_account(evaluator_bidder), - project_metadata_2.clone().policy_ipfs_cid.unwrap() - ), - project_id_2, - evaluator_bid.amount, - evaluator_bid.multiplier, - evaluator_bid.asset - ), - Error::::ParticipantNotEnoughFunds - ); - }); - } - - #[test] - fn cannot_bid_before_auction_round() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let _ = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - let did = generate_did_from_account(BIDDER_2); - let investor_type = InvestorType::Institutional; - - inst.execute(|| { - assert_noop!( - PolimecFunding::do_bid( - &BIDDER_2, - 0, - 1, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - did, - investor_type, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - Error::::IncorrectRound - ); - }); - } - - #[test] - fn cannot_bid_more_than_project_limit_count() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.mainnet_token_max_supply = 1_000_000_000 * CT_UNIT; - project_metadata.total_allocation_size = 100_000_000 * CT_UNIT; - - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), vec![EVALUATOR_1], vec![100u8]); - let max_bids_per_project: u32 = ::MaxBidsPerProject::get(); - let bids = - (0u32..max_bids_per_project - 1).map(|i| (i as u32 + 420u32, 5000 * CT_UNIT).into()).collect_vec(); - - let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations); - - let plmc_for_bidding = inst.calculate_auction_plmc_charged_with_given_price( - &bids.clone(), - project_metadata.minimum_price, - true, - ); - let usdt_for_bidding = inst.calculate_auction_funding_asset_charged_with_given_price( - &bids.clone(), - project_metadata.minimum_price, - ); - - inst.mint_plmc_to(plmc_for_bidding.clone()); - inst.mint_foreign_asset_to(usdt_for_bidding.clone()); - inst.bid_for_users(project_id, bids.clone()).unwrap(); - - let current_bucket = inst.execute(|| Buckets::::get(project_id)).unwrap(); - let remaining_ct = current_bucket.amount_left; - - // This bid should be split in 2, but the second one should fail, making the whole extrinsic fail and roll back storage - let failing_bid = - BidParams::::new(BIDDER_1, remaining_ct + 5000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let plmc_for_failing_bid = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &vec![failing_bid.clone()], - project_metadata.clone(), - Some(current_bucket), - true, - ); - - let usdt_for_bidding = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &vec![failing_bid.clone()], - project_metadata.clone(), - Some(current_bucket), - ); - - inst.mint_plmc_to(plmc_for_failing_bid.clone()); - inst.mint_foreign_asset_to(usdt_for_bidding.clone()); - - inst.execute(|| { - assert_noop!( - PolimecFunding::bid( - RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt_with_cid( - failing_bid.bidder, - InvestorType::Professional, - generate_did_from_account(failing_bid.bidder), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - failing_bid.amount, - failing_bid.multiplier, - failing_bid.asset - ), - Error::::TooManyProjectParticipations - ); - }); - - // Now we test that after reaching the limit, just one bid is also not allowed - inst.execute(|| { - assert_ok!(PolimecFunding::bid( - RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt_with_cid( - failing_bid.bidder, - InvestorType::Professional, - generate_did_from_account(failing_bid.bidder), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - remaining_ct, - failing_bid.multiplier, - failing_bid.asset - )); - }); - inst.execute(|| { - assert_noop!( - PolimecFunding::bid( - RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt_with_cid( - failing_bid.bidder, - InvestorType::Professional, - generate_did_from_account(failing_bid.bidder), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - 5000 * CT_UNIT, - failing_bid.multiplier, - failing_bid.asset - ), - Error::::TooManyProjectParticipations - ); - }); - } - - #[test] - fn cannot_bid_more_than_user_limit_count() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.mainnet_token_max_supply = 1_000_000_000 * CT_UNIT; - project_metadata.total_allocation_size = 100_000_000 * CT_UNIT; - - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), vec![EVALUATOR_1], vec![100u8]); - let max_bids_per_user: u32 = ::MaxBidsPerUser::get(); - let bids = (0u32..max_bids_per_user - 1u32).map(|_| (BIDDER_1, 5000 * CT_UNIT).into()).collect_vec(); - - let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations); - - let plmc_for_bidding = inst.calculate_auction_plmc_charged_with_given_price( - &bids.clone(), - project_metadata.minimum_price, - true, - ); - let usdt_for_bidding = inst.calculate_auction_funding_asset_charged_with_given_price( - &bids.clone(), - project_metadata.minimum_price, - ); - - inst.mint_plmc_to(plmc_for_bidding.clone()); - inst.mint_foreign_asset_to(usdt_for_bidding.clone()); - inst.bid_for_users(project_id, bids.clone()).unwrap(); - - let current_bucket = inst.execute(|| Buckets::::get(project_id)).unwrap(); - let remaining_ct = current_bucket.amount_left; - - // This bid should be split in 2, but the second one should fail, making the whole extrinsic fail and roll back storage - let failing_bid = - BidParams::::new(BIDDER_1, remaining_ct + 5000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let plmc_for_failing_bid = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &vec![failing_bid.clone()], - project_metadata.clone(), - Some(current_bucket), - true, - ); - let usdt_for_bidding = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &vec![failing_bid.clone()], - project_metadata.clone(), - Some(current_bucket), - ); - inst.mint_plmc_to(plmc_for_failing_bid.clone()); - inst.mint_foreign_asset_to(usdt_for_bidding.clone()); - - inst.execute(|| { - assert_noop!( - PolimecFunding::bid( - RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt_with_cid( - failing_bid.bidder, - InvestorType::Professional, - generate_did_from_account(failing_bid.bidder), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - failing_bid.amount, - failing_bid.multiplier, - failing_bid.asset - ), - Error::::TooManyUserParticipations - ); - }); - - // Now we test that after reaching the limit, just one bid is also not allowed - inst.execute(|| { - assert_ok!(PolimecFunding::bid( - RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt_with_cid( - failing_bid.bidder, - InvestorType::Professional, - generate_did_from_account(failing_bid.bidder), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - remaining_ct, - failing_bid.multiplier, - failing_bid.asset - )); - }); - inst.execute(|| { - assert_noop!( - PolimecFunding::bid( - RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt_with_cid( - failing_bid.bidder, - InvestorType::Professional, - generate_did_from_account(failing_bid.bidder), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - 5000 * CT_UNIT, - failing_bid.multiplier, - failing_bid.asset - ), - Error::::TooManyUserParticipations - ); - }); - } - - #[test] - fn per_credential_type_ticket_size_minimums() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.total_allocation_size = 100_000 * CT_UNIT; - project_metadata.bidding_ticket_sizes = BiddingTicketSizes { - professional: TicketSize::new(8_000 * USD_UNIT, None), - institutional: TicketSize::new(20_000 * USD_UNIT, None), - phantom: Default::default(), - }; - - let evaluations = default_evaluations(); - - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations.clone()); - - inst.mint_plmc_to(vec![(BIDDER_1, 50_000 * CT_UNIT).into(), (BIDDER_2, 50_000 * CT_UNIT).into()]); - - inst.mint_foreign_asset_to(vec![ - (BIDDER_1, 50_000 * USD_UNIT).into(), - (BIDDER_2, 50_000 * USD_UNIT).into(), - ]); - - // bid below 800 CT (8k USD) should fail for professionals - inst.execute(|| { - assert_noop!( - Pallet::::do_bid( - &BIDDER_1, - project_id, - 799 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(BIDDER_1), - InvestorType::Professional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - Error::::TooLow - ); - }); - // bid below 2000 CT (20k USD) should fail for institutionals - inst.execute(|| { - assert_noop!( - Pallet::::do_bid( - &BIDDER_2, - project_id, - 1999 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(BIDDER_1), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - Error::::TooLow - ); - }); - } - - #[test] - fn ticket_size_minimums_use_current_bucket_price() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.total_allocation_size = 100_000 * CT_UNIT; - project_metadata.bidding_ticket_sizes = BiddingTicketSizes { - professional: TicketSize::new(8_000 * USD_UNIT, None), - institutional: TicketSize::new(20_000 * USD_UNIT, None), - phantom: Default::default(), - }; - project_metadata.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(1.0), - USD_DECIMALS, - project_metadata.clone().token_information.decimals, - ) - .unwrap(); - - let evaluations = default_evaluations(); - - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations.clone()); - - inst.mint_plmc_to(vec![ - (BIDDER_1, 200_000 * PLMC).into(), - (BIDDER_2, 200_000 * PLMC).into(), - (BIDDER_3, 200_000 * PLMC).into(), - ]); - inst.mint_foreign_asset_to(vec![ - (BIDDER_1, 200_000 * USDT_UNIT).into(), - (BIDDER_2, 200_000 * USDT_UNIT).into(), - (BIDDER_3, 200_000 * USDT_UNIT).into(), - ]); - - // First bucket is covered by one bidder - let big_bid: BidParams = (BIDDER_1, 50_000 * CT_UNIT).into(); - inst.bid_for_users(project_id, vec![big_bid.clone()]).unwrap(); - - // A bid at the min price of 1 should require a min of 8k CT, but with a new price of 1.1, we can now bid with less - let bucket_increase_price = PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(1.1), - USD_DECIMALS, - project_metadata.clone().token_information.decimals, - ) - .unwrap(); - let smallest_ct_amount_at_8k_usd = bucket_increase_price - .reciprocal() - .unwrap() - .checked_mul_int(8000 * USD_UNIT) - // add 1 because result could be .99999 of what we expect - .unwrap() + 1; - assert!(smallest_ct_amount_at_8k_usd < 8000 * CT_UNIT); - inst.execute(|| { - assert_ok!(Pallet::::do_bid( - &BIDDER_1, - project_id, - smallest_ct_amount_at_8k_usd, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(BIDDER_1), - InvestorType::Professional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )); - }); - let smallest_ct_amount_at_20k_usd = bucket_increase_price - .reciprocal() - .unwrap() - .checked_mul_int(20_000 * USD_UNIT) - // add 1 because result could be .99999 of what we expect - .unwrap() + 1; - assert!(smallest_ct_amount_at_20k_usd < 20_000 * CT_UNIT); - inst.execute(|| { - assert_ok!(Pallet::::do_bid( - &BIDDER_2, - project_id, - smallest_ct_amount_at_20k_usd, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(BIDDER_1), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )); - }); - } - - #[test] - fn per_credential_type_ticket_size_maximums() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.bidding_ticket_sizes = BiddingTicketSizes { - professional: TicketSize::new(8_000 * USD_UNIT, Some(100_000 * USD_UNIT)), - institutional: TicketSize::new(20_000 * USD_UNIT, Some(500_000 * USD_UNIT)), - phantom: Default::default(), - }; - project_metadata.contributing_ticket_sizes = ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, Some(100_000 * USD_UNIT)), - professional: TicketSize::new(USD_UNIT, Some(20_000 * USD_UNIT)), - institutional: TicketSize::new(USD_UNIT, Some(50_000 * USD_UNIT)), - phantom: Default::default(), - }; - let evaluations = default_evaluations(); - - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations.clone()); - - inst.mint_plmc_to(vec![ - (BIDDER_1, 500_000 * CT_UNIT).into(), - (BIDDER_2, 500_000 * CT_UNIT).into(), - (BIDDER_3, 500_000 * CT_UNIT).into(), - (BIDDER_4, 500_000 * CT_UNIT).into(), - ]); - - inst.mint_foreign_asset_to(vec![ - (BIDDER_1, 500_000 * USD_UNIT).into(), - (BIDDER_2, 500_000 * USD_UNIT).into(), - (BIDDER_3, 500_000 * USD_UNIT).into(), - (BIDDER_4, 500_000 * USD_UNIT).into(), - ]); - - let bidder_1_jwt = get_mock_jwt_with_cid( - BIDDER_1, - InvestorType::Professional, - generate_did_from_account(BIDDER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let bidder_2_jwt_same_did = get_mock_jwt_with_cid( - BIDDER_2, - InvestorType::Professional, - generate_did_from_account(BIDDER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - // total bids with same DID above 10k CT (100k USD) should fail for professionals - inst.execute(|| { - assert_ok!(Pallet::::bid( - RuntimeOrigin::signed(BIDDER_1), - bidder_1_jwt, - project_id, - 8000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::bid( - RuntimeOrigin::signed(BIDDER_2), - bidder_2_jwt_same_did.clone(), - project_id, - 3000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT - ), - Error::::TooHigh - ); - }); - // bidding 10k total works - inst.execute(|| { - assert_ok!(Pallet::::bid( - RuntimeOrigin::signed(BIDDER_2), - bidder_2_jwt_same_did, - project_id, - 2000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - - let bidder_3_jwt = get_mock_jwt_with_cid( - BIDDER_3, - InvestorType::Institutional, - generate_did_from_account(BIDDER_3), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let bidder_4_jwt_same_did = get_mock_jwt_with_cid( - BIDDER_4, - InvestorType::Institutional, - generate_did_from_account(BIDDER_3), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - // total bids with same DID above 50k CT (500k USD) should fail for institutionals - inst.execute(|| { - assert_ok!(Pallet::::bid( - RuntimeOrigin::signed(BIDDER_3), - bidder_3_jwt, - project_id, - 40_000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::bid( - RuntimeOrigin::signed(BIDDER_4), - bidder_4_jwt_same_did.clone(), - project_id, - 11_000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooHigh - ); - }); - // bidding 50k total works - inst.execute(|| { - assert_ok!(Pallet::::bid( - RuntimeOrigin::signed(BIDDER_4), - bidder_4_jwt_same_did, - project_id, - 10_000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - } - - #[test] - fn issuer_cannot_bid_his_project() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); - assert_err!( - inst.execute(|| crate::Pallet::::do_bid( - &(&ISSUER_1 + 1), - project_id, - 500 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(ISSUER_1), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )), - Error::::ParticipationToOwnProject - ); - } - - #[test] - fn bid_with_asset_not_accepted() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); - let bids = vec![BidParams::::new(BIDDER_1, 10_000, 1u8, AcceptedFundingAsset::USDC)]; - - let did = generate_did_from_account(bids[0].bidder); - let investor_type = InvestorType::Institutional; - - let outcome = inst.execute(|| { - Pallet::::do_bid( - &bids[0].bidder, - project_id, - bids[0].amount, - bids[0].multiplier, - bids[0].asset, - did, - investor_type, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ) - }); - frame_support::assert_err!(outcome, Error::::FundingAssetNotAccepted); - } - - #[test] - fn wrong_policy_on_jwt() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); - - inst.execute(|| { - assert_noop!( - PolimecFunding::bid( - RuntimeOrigin::signed(BIDDER_1), - get_mock_jwt_with_cid( - BIDDER_1, - InvestorType::Professional, - generate_did_from_account(BIDDER_1), - "wrong_cid".as_bytes().to_vec().try_into().unwrap() - ), - project_id, - 5000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT - ), - Error::::PolicyMismatch - ); - }); - } - } -} +// use super::*; +// +// #[cfg(test)] +// mod round_flow { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use frame_support::traits::fungibles::metadata::Inspect; +// use sp_core::bounded_vec; +// use std::{collections::HashSet, ops::Not}; +// +// #[test] +// fn auction_round_completed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let _project_id = +// inst.create_community_contributing_project(project_metadata, ISSUER_1, None, evaluations, bids); +// } +// +// #[test] +// fn multiple_auction_projects_completed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project1 = default_project_metadata(ISSUER_1); +// let project2 = default_project_metadata(ISSUER_2); +// let project3 = default_project_metadata(ISSUER_3); +// let project4 = default_project_metadata(ISSUER_4); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// +// inst.create_community_contributing_project(project1, ISSUER_1, None, evaluations.clone(), bids.clone()); +// inst.create_community_contributing_project(project2, ISSUER_2, None, evaluations.clone(), bids.clone()); +// inst.create_community_contributing_project(project3, ISSUER_3, None, evaluations.clone(), bids.clone()); +// inst.create_community_contributing_project(project4, ISSUER_4, None, evaluations, bids); +// } +// +// #[test] +// fn wap_is_accurate() { +// // From the knowledge hub: https://hub.polimec.org/learn/calculation-example#auction-round-calculation-example +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// const ADAM: u32 = 60; +// const TOM: u32 = 61; +// const SOFIA: u32 = 62; +// const FRED: u32 = 63; +// const ANNA: u32 = 64; +// const DAMIAN: u32 = 65; +// +// let accounts = vec![ADAM, TOM, SOFIA, FRED, ANNA, DAMIAN]; +// +// let bounded_name = bounded_name(); +// let bounded_symbol = bounded_symbol(); +// let metadata_hash = ipfs_hash(); +// let normalized_price = PriceOf::::from_float(10.0); +// let decimal_aware_price = PriceProviderOf::::calculate_decimals_aware_price( +// normalized_price, +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(); +// let project_metadata = ProjectMetadata { +// token_information: CurrencyMetadata { +// name: bounded_name, +// symbol: bounded_symbol, +// decimals: CT_DECIMALS, +// }, +// mainnet_token_max_supply: 8_000_000 * CT_UNIT, +// total_allocation_size: 100_000 * CT_UNIT, +// auction_round_allocation_percentage: Percent::from_percent(50u8), +// minimum_price: decimal_aware_price, +// bidding_ticket_sizes: BiddingTicketSizes { +// professional: TicketSize::new(5000 * USD_UNIT, None), +// institutional: TicketSize::new(5000 * USD_UNIT, None), +// phantom: Default::default(), +// }, +// contributing_ticket_sizes: ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, None), +// professional: TicketSize::new(USD_UNIT, None), +// institutional: TicketSize::new(USD_UNIT, None), +// phantom: Default::default(), +// }, +// participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), +// funding_destination_account: ISSUER_1, +// policy_ipfs_cid: Some(metadata_hash), +// }; +// +// // overfund with plmc +// let plmc_fundings = accounts +// .iter() +// .map(|acc| UserToPLMCBalance { account: acc.clone(), plmc_amount: PLMC * 1_000_000 }) +// .collect_vec(); +// let usdt_fundings = accounts +// .iter() +// .map(|acc| UserToForeignAssets { +// account: acc.clone(), +// asset_amount: USD_UNIT * 1_000_000, +// asset_id: AcceptedFundingAsset::USDT.to_assethub_id(), +// }) +// .collect_vec(); +// inst.mint_plmc_to(plmc_fundings); +// inst.mint_foreign_asset_to(usdt_fundings); +// +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); +// +// let bids = vec![ +// (ADAM, 10_000 * CT_UNIT).into(), +// (TOM, 20_000 * CT_UNIT).into(), +// (SOFIA, 20_000 * CT_UNIT).into(), +// (FRED, 10_000 * CT_UNIT).into(), +// (ANNA, 5_000 * CT_UNIT).into(), +// (DAMIAN, 5_000 * CT_UNIT).into(), +// ]; +// +// inst.bid_for_users(project_id, bids).unwrap(); +// +// inst.start_community_funding(project_id).unwrap(); +// +// let token_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let normalized_wap = +// PriceProviderOf::::convert_back_to_normal_price(token_price, USD_DECIMALS, CT_DECIMALS) +// .unwrap(); +// let desired_price = PriceOf::::from_float(11.1818f64); +// +// assert_close_enough!( +// normalized_wap.saturating_mul_int(CT_UNIT), +// desired_price.saturating_mul_int(CT_UNIT), +// Perquintill::from_float(0.99) +// ); +// } +// +// #[test] +// fn bids_at_higher_price_than_weighted_average_use_average() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = default_evaluations(); +// let mut bids: Vec> = inst.generate_bids_from_total_usd( +// project_metadata.minimum_price.saturating_mul_int( +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size, +// ), +// project_metadata.minimum_price, +// default_weights(), +// default_bidders(), +// default_bidder_multipliers(), +// ); +// +// let second_bucket_bid = (BIDDER_6, 500 * CT_UNIT).into(); +// bids.push(second_bucket_bid); +// +// let project_id = +// inst.create_community_contributing_project(project_metadata.clone(), issuer, None, evaluations, bids); +// let bidder_5_bid = +// inst.execute(|| Bids::::iter_prefix_values((project_id, BIDDER_6)).next().unwrap()); +// let wabgp = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let price_normalized = ::PriceProvider::convert_back_to_normal_price( +// bidder_5_bid.original_ct_usd_price, +// USD_DECIMALS, +// project_metadata.token_information.decimals, +// ) +// .unwrap(); +// assert_eq!(price_normalized.to_float(), 11.0); +// assert_eq!(bidder_5_bid.final_ct_usd_price, wabgp); +// } +// +// #[test] +// fn auction_gets_percentage_of_ct_total_allocation() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let evaluations = default_evaluations(); +// let auction_percentage = project_metadata.auction_round_allocation_percentage; +// let total_allocation = project_metadata.total_allocation_size; +// +// let auction_allocation = auction_percentage * total_allocation; +// +// let bids = vec![(BIDDER_1, auction_allocation).into()]; +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// evaluations.clone(), +// bids, +// ); +// let mut bid_infos = Bids::::iter_prefix_values((project_id,)); +// let bid_info = inst.execute(|| bid_infos.next().unwrap()); +// assert!(inst.execute(|| bid_infos.next().is_none())); +// assert_eq!(bid_info.final_ct_amount, auction_allocation); +// +// let project_metadata = default_project_metadata(ISSUER_2); +// let bids = vec![(BIDDER_1, auction_allocation).into(), (BIDDER_1, 1000 * CT_UNIT).into()]; +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_2, +// None, +// evaluations.clone(), +// bids, +// ); +// let mut bid_infos = Bids::::iter_prefix_values((project_id,)); +// let bid_info_1 = inst.execute(|| bid_infos.next().unwrap()); +// let bid_info_2 = inst.execute(|| bid_infos.next().unwrap()); +// assert!(inst.execute(|| bid_infos.next().is_none())); +// assert_eq!( +// bid_info_1.final_ct_amount + bid_info_2.final_ct_amount, +// auction_allocation, +// "Should not be able to buy more than auction allocation" +// ); +// } +// +// // Partial acceptance at price <= wap (refund due to less CT bought) +// // Full Acceptance at price > wap (refund due to final price lower than original price paid) +// // Rejection due to no more tokens left (full refund) +// #[test] +// fn bids_get_rejected_and_refunded_part_one() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = default_evaluations(); +// +// let bid_1 = BidParams::new(BIDDER_1, 5000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let bid_2 = BidParams::new(BIDDER_2, 40_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let bid_3 = BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let bid_4 = BidParams::new(BIDDER_3, 6000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let bid_5 = BidParams::new(BIDDER_4, 2000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// // post bucketing, the bids look like this: +// // (BIDDER_1, 5k) - (BIDDER_2, 40k) - (BIDDER_1, 5k) - (BIDDER_1, 5k) - (BIDDER_3 - 5k) - (BIDDER_3 - 1k) - (BIDDER_4 - 2k) +// // | -------------------- 1USD ----------------------|---- 1.1 USD ---|---- 1.2 USD ----|----------- 1.3 USD -------------| +// // post wap ~ 1.0557252: +// // (Accepted, 5k) - (Partially, 32k) - (Rejected, 5k) - (Accepted, 5k) - (Accepted - 5k) - (Accepted - 1k) - (Accepted - 2k) +// +// let bids = vec![bid_1, bid_2, bid_3, bid_4, bid_5]; +// +// let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); +// +// let plmc_fundings = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata.clone(), +// None, +// false, +// ); +// let usdt_fundings = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata.clone(), +// None, +// ); +// +// let plmc_existential_amounts = plmc_fundings.accounts().existential_deposits(); +// +// inst.mint_plmc_to(plmc_fundings.clone()); +// inst.mint_plmc_to(plmc_existential_amounts.clone()); +// inst.mint_foreign_asset_to(usdt_fundings.clone()); +// +// inst.bid_for_users(project_id, bids.clone()).unwrap(); +// +// inst.do_free_plmc_assertions(vec![ +// UserToPLMCBalance::new(BIDDER_1, inst.get_ed()), +// UserToPLMCBalance::new(BIDDER_2, inst.get_ed()), +// ]); +// inst.do_reserved_plmc_assertions(plmc_fundings.clone(), HoldReason::Participation(project_id).into()); +// inst.do_bid_transferred_foreign_asset_assertions(usdt_fundings.clone(), project_id); +// +// inst.start_community_funding(project_id).unwrap(); +// +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let returned_auction_plmc = +// inst.calculate_auction_plmc_returned_from_all_bids_made(&bids, project_metadata.clone(), wap); +// let returned_funding_assets = +// inst.calculate_auction_funding_asset_returned_from_all_bids_made(&bids, project_metadata, wap); +// +// let expected_free_plmc = inst.generic_map_operation( +// vec![returned_auction_plmc.clone(), plmc_existential_amounts], +// MergeOperation::Add, +// ); +// let expected_free_funding_assets = +// inst.generic_map_operation(vec![returned_funding_assets.clone()], MergeOperation::Add); +// let expected_reserved_plmc = inst +// .generic_map_operation(vec![plmc_fundings.clone(), returned_auction_plmc], MergeOperation::Subtract); +// let expected_held_funding_assets = inst +// .generic_map_operation(vec![usdt_fundings.clone(), returned_funding_assets], MergeOperation::Subtract); +// +// inst.do_free_plmc_assertions(expected_free_plmc); +// +// inst.do_reserved_plmc_assertions(expected_reserved_plmc, HoldReason::Participation(project_id).into()); +// +// inst.do_free_foreign_asset_assertions(expected_free_funding_assets); +// inst.do_bid_transferred_foreign_asset_assertions(expected_held_funding_assets, project_id); +// } +// +// #[test] +// // Partial acceptance at price > wap (refund due to less CT bought, and final price lower than original price paid) +// // Rejection due to bid being made after random end (full refund) +// fn bids_get_rejected_and_refunded_part_two() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); +// +// let total_auction_ct_amount = +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; +// +// let full_ct_bid_rejected = +// BidParams::new(BIDDER_1, total_auction_ct_amount, 1u8, AcceptedFundingAsset::USDT); +// let full_ct_bid_partially_accepted = +// BidParams::new(BIDDER_2, total_auction_ct_amount, 1u8, AcceptedFundingAsset::USDT); +// let oversubscription_bid = BidParams::new(BIDDER_3, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let after_random_end_bid = BidParams::new(BIDDER_4, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// +// let all_bids = vec![ +// full_ct_bid_rejected.clone(), +// full_ct_bid_partially_accepted.clone(), +// oversubscription_bid.clone(), +// after_random_end_bid.clone(), +// ]; +// let all_included_bids = +// vec![full_ct_bid_rejected.clone(), full_ct_bid_partially_accepted.clone(), oversubscription_bid]; +// +// let necessary_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &all_bids, +// project_metadata.clone(), +// None, +// false, +// ); +// let plmc_existential_amounts = necessary_plmc.accounts().existential_deposits(); +// let necessary_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &all_bids, +// project_metadata.clone(), +// None, +// ); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// inst.mint_plmc_to(plmc_existential_amounts.clone()); +// inst.mint_foreign_asset_to(necessary_usdt.clone()); +// inst.bid_for_users(project_id, all_included_bids.clone()).unwrap(); +// inst.advance_time( +// ::AuctionOpeningDuration::get() + +// ::AuctionClosingDuration::get() - +// 1, +// ) +// .unwrap(); +// +// inst.bid_for_users(project_id, vec![after_random_end_bid]).unwrap(); +// inst.do_free_plmc_assertions(vec![ +// UserToPLMCBalance::new(BIDDER_1, inst.get_ed()), +// UserToPLMCBalance::new(BIDDER_2, inst.get_ed()), +// UserToPLMCBalance::new(BIDDER_3, inst.get_ed()), +// UserToPLMCBalance::new(BIDDER_4, inst.get_ed()), +// ]); +// inst.do_reserved_plmc_assertions(necessary_plmc.clone(), HoldReason::Participation(project_id).into()); +// inst.do_bid_transferred_foreign_asset_assertions(necessary_usdt.clone(), project_id); +// inst.start_community_funding(project_id).unwrap(); +// +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let plmc_returned = inst.calculate_auction_plmc_returned_from_all_bids_made( +// &all_included_bids, +// project_metadata.clone(), +// wap, +// ); +// let usdt_returned = inst.calculate_auction_funding_asset_returned_from_all_bids_made( +// &all_included_bids, +// project_metadata.clone(), +// wap, +// ); +// +// let rejected_bid_necessary_plmc = &necessary_plmc[3]; +// let rejected_bid_necessary_usdt = &necessary_usdt[3]; +// +// let expected_free = inst.generic_map_operation( +// vec![plmc_returned.clone(), plmc_existential_amounts, vec![rejected_bid_necessary_plmc.clone()]], +// MergeOperation::Add, +// ); +// inst.do_free_plmc_assertions(expected_free); +// let expected_reserved = inst.generic_map_operation( +// vec![necessary_plmc.clone(), plmc_returned.clone(), vec![rejected_bid_necessary_plmc.clone()]], +// MergeOperation::Subtract, +// ); +// inst.do_reserved_plmc_assertions(expected_reserved, HoldReason::Participation(project_id).into()); +// let expected_reserved = inst.generic_map_operation( +// vec![necessary_usdt.clone(), usdt_returned.clone(), vec![rejected_bid_necessary_usdt.clone()]], +// MergeOperation::Subtract, +// ); +// inst.do_bid_transferred_foreign_asset_assertions(expected_reserved, project_id); +// } +// +// #[test] +// fn no_bids_made() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = default_evaluations(); +// let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); +// +// let details = inst.get_project_details(project_id); +// let opening_end = details.round_duration.end().unwrap(); +// let now = inst.current_block(); +// inst.advance_time(opening_end - now + 2).unwrap(); +// +// let details = inst.get_project_details(project_id); +// let closing_end = details.round_duration.end().unwrap(); +// let now = inst.current_block(); +// inst.advance_time(closing_end - now + 2).unwrap(); +// +// let details = inst.get_project_details(project_id); +// assert!(matches!(details.status, ProjectStatus::CommunityRound(..))); +// assert_eq!(details.weighted_average_price, Some(project_metadata.minimum_price)); +// } +// +// #[test] +// fn all_bids_rejected() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); +// +// let necessary_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata.clone(), +// None, +// true, +// ); +// let necessary_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata.clone(), +// None, +// ); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// inst.mint_foreign_asset_to(necessary_usdt.clone()); +// inst.advance_time( +// ::AuctionOpeningDuration::get() + +// ::AuctionClosingDuration::get() - +// 1, +// ) +// .unwrap(); +// +// // We bid at the last block, which we assume will be after the random end +// inst.bid_for_users(project_id, bids.clone()).unwrap(); +// +// inst.start_community_funding(project_id).unwrap(); +// +// let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); +// // let non_rejected_bids = stored_bids +// // .into_iter() +// // .filter(|bid| { +// // (bid.final_ct_amount == 0 && bid.status == BidStatus::Rejected(RejectionReason::RejectionReason)) +// // .not() +// // }) +// // .count(); +// // assert_eq!(non_rejected_bids, 0); +// // assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound); +// } +// +// #[test] +// fn wap_from_different_funding_assets() { +// // From the knowledge hub: https://hub.polimec.org/learn/calculation-example#auction-round-calculation-example +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// const ADAM: u32 = 60; +// const TOM: u32 = 61; +// const SOFIA: u32 = 62; +// const FRED: u32 = 63; +// const ANNA: u32 = 64; +// const DAMIAN: u32 = 65; +// +// let accounts = vec![ADAM, TOM, SOFIA, FRED, ANNA, DAMIAN]; +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.total_allocation_size = 100_000 * CT_UNIT; +// project_metadata.participation_currencies = +// bounded_vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT,]; +// +// // overfund with plmc +// let plmc_fundings = accounts +// .iter() +// .map(|acc| UserToPLMCBalance { account: acc.clone(), plmc_amount: PLMC * 1_000_000 }) +// .collect_vec(); +// +// let fundings = [AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT]; +// assert_eq!(fundings.len(), AcceptedFundingAsset::VARIANT_COUNT); +// let mut fundings = fundings.into_iter().cycle(); +// +// let usdt_fundings = accounts +// .iter() +// .map(|acc| { +// let accepted_asset = fundings.next().unwrap(); +// let asset_id = accepted_asset.to_assethub_id(); +// let asset_decimals = inst.execute(|| ::FundingCurrency::decimals(asset_id)); +// let asset_unit = 10u128.checked_pow(asset_decimals.into()).unwrap(); +// UserToForeignAssets { account: acc.clone(), asset_amount: asset_unit * 1_000_000, asset_id } +// }) +// .collect_vec(); +// inst.mint_plmc_to(plmc_fundings); +// inst.mint_foreign_asset_to(usdt_fundings); +// +// let project_id = inst.create_auctioning_project(project_metadata, ISSUER_1, None, default_evaluations()); +// +// let bids = vec![ +// (ADAM, 10_000 * CT_UNIT, 1, AcceptedFundingAsset::USDT).into(), +// (TOM, 20_000 * CT_UNIT, 1, AcceptedFundingAsset::USDC).into(), +// (SOFIA, 20_000 * CT_UNIT, 1, AcceptedFundingAsset::DOT).into(), +// (FRED, 10_000 * CT_UNIT, 1, AcceptedFundingAsset::USDT).into(), +// (ANNA, 5_000 * CT_UNIT, 1, AcceptedFundingAsset::USDC).into(), +// (DAMIAN, 5_000 * CT_UNIT, 1, AcceptedFundingAsset::DOT).into(), +// ]; +// +// inst.bid_for_users(project_id, bids).unwrap(); +// +// inst.start_community_funding(project_id).unwrap(); +// +// let token_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let normalized_wap = +// PriceProviderOf::::convert_back_to_normal_price(token_price, USD_DECIMALS, CT_DECIMALS) +// .unwrap(); +// +// let desired_price = PriceOf::::from_float(11.1818f64); +// +// assert_close_enough!( +// normalized_wap.saturating_mul_int(USD_UNIT), +// desired_price.saturating_mul_int(USD_UNIT), +// Perquintill::from_float(0.99) +// ); +// } +// +// #[test] +// fn different_decimals_ct_works_as_expected() { +// // Setup some base values to compare different decimals +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let ed = inst.get_ed(); +// let default_project_metadata = default_project_metadata(ISSUER_1); +// let original_decimal_aware_price = default_project_metadata.minimum_price; +// let original_price = ::PriceProvider::convert_back_to_normal_price( +// original_decimal_aware_price, +// USD_DECIMALS, +// default_project_metadata.token_information.decimals, +// ) +// .unwrap(); +// let usable_plmc_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// PLMC_FOREIGN_ID, +// USD_DECIMALS, +// PLMC_DECIMALS, +// ) +// .unwrap() +// }); +// let usdt_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::USDT.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::USDT.to_assethub_id()), +// ) +// .unwrap() +// }); +// let usdc_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::USDC.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::USDC.to_assethub_id()), +// ) +// .unwrap() +// }); +// let dot_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::DOT.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::DOT.to_assethub_id()), +// ) +// .unwrap() +// }); +// +// let mut funding_assets_cycle = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] +// .into_iter() +// .cycle(); +// +// let mut min_bid_amounts_ct = Vec::new(); +// let mut min_bid_amounts_usd = Vec::new(); +// let mut auction_allocations_ct = Vec::new(); +// let mut auction_allocations_usd = Vec::new(); +// +// let mut decimal_test = |decimals: u8| { +// let funding_asset = funding_assets_cycle.next().unwrap(); +// let funding_asset_usd_price = match funding_asset { +// AcceptedFundingAsset::USDT => usdt_price, +// AcceptedFundingAsset::USDC => usdc_price, +// AcceptedFundingAsset::DOT => dot_price, +// }; +// +// let mut project_metadata = default_project_metadata.clone(); +// project_metadata.token_information.decimals = decimals; +// project_metadata.minimum_price = +// ::PriceProvider::calculate_decimals_aware_price( +// original_price, +// USD_DECIMALS, +// decimals, +// ) +// .unwrap(); +// +// project_metadata.total_allocation_size = 1_000_000 * 10u128.pow(decimals as u32); +// project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; +// project_metadata.participation_currencies = bounded_vec!(funding_asset); +// +// let issuer: AccountIdOf = (10_000 + inst.get_new_nonce()).try_into().unwrap(); +// let evaluations = inst.generate_successful_evaluations( +// project_metadata.clone(), +// default_evaluators(), +// default_weights(), +// ); +// let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); +// +// let auction_allocation_percentage = project_metadata.auction_round_allocation_percentage; +// let auction_allocation_ct = auction_allocation_percentage * project_metadata.total_allocation_size; +// auction_allocations_ct.push(auction_allocation_ct); +// let auction_allocation_usd = project_metadata.minimum_price.saturating_mul_int(auction_allocation_ct); +// auction_allocations_usd.push(auction_allocation_usd); +// +// let min_professional_bid_usd = +// project_metadata.bidding_ticket_sizes.professional.usd_minimum_per_participation; +// min_bid_amounts_usd.push(min_professional_bid_usd); +// let min_professional_bid_ct = +// project_metadata.minimum_price.reciprocal().unwrap().saturating_mul_int(min_professional_bid_usd); +// let min_professional_bid_plmc = +// usable_plmc_price.reciprocal().unwrap().saturating_mul_int(min_professional_bid_usd); +// min_bid_amounts_ct.push(min_professional_bid_ct); +// let min_professional_bid_funding_asset = +// funding_asset_usd_price.reciprocal().unwrap().saturating_mul_int(min_professional_bid_usd); +// +// // Every project should want to raise 5MM USD on the auction round regardless of CT decimals +// assert_eq!(auction_allocation_usd, 5_000_000 * USD_UNIT); +// +// // A minimum bid goes through. This is a fixed USD value, but the extrinsic amount depends on CT decimals. +// inst.mint_plmc_to(vec![UserToPLMCBalance::new(BIDDER_1, min_professional_bid_plmc + ed)]); +// inst.mint_foreign_asset_to(vec![UserToForeignAssets::new( +// BIDDER_1, +// min_professional_bid_funding_asset, +// funding_asset.to_assethub_id(), +// )]); +// +// assert_ok!(inst.execute(|| PolimecFunding::bid( +// RuntimeOrigin::signed(BIDDER_1), +// get_mock_jwt_with_cid( +// BIDDER_1, +// InvestorType::Professional, +// generate_did_from_account(BIDDER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// min_professional_bid_ct, +// 1u8.try_into().unwrap(), +// funding_asset, +// ))); +// +// // The bucket should have 50% of 1MM * 10^decimals CT minus what we just bid +// let bucket = inst.execute(|| Buckets::::get(project_id).unwrap()); +// assert_eq!(bucket.amount_left, 500_000u128 * 10u128.pow(decimals as u32) - min_professional_bid_ct); +// }; +// +// for decimals in 6..=18 { +// decimal_test(decimals); +// } +// +// // Since we use the same original price and allocation size and adjust for decimals, +// // the USD amounts should be the same +// assert!(min_bid_amounts_usd.iter().all(|x| *x == min_bid_amounts_usd[0])); +// assert!(auction_allocations_usd.iter().all(|x| *x == auction_allocations_usd[0])); +// +// // CT amounts however should be different from each other +// let mut hash_set_1 = HashSet::new(); +// for amount in min_bid_amounts_ct { +// assert!(!hash_set_1.contains(&amount)); +// hash_set_1.insert(amount); +// } +// let mut hash_set_2 = HashSet::new(); +// for amount in auction_allocations_ct { +// assert!(!hash_set_2.contains(&amount)); +// hash_set_2.insert(amount); +// } +// } +// +// #[test] +// fn all_bids_but_one_have_price_higher_than_wap() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let total_allocation = 10_000_000 * CT_UNIT; +// let min_bid_ct = 500 * CT_UNIT; // 5k USD at 10USD/CT +// let max_bids_per_project: u32 = ::MaxBidsPerProject::get(); +// let big_bid: BidParams = (BIDDER_1, total_allocation).into(); +// let small_bids: Vec> = +// (0..max_bids_per_project - 1).map(|i| (i + BIDDER_1, min_bid_ct).into()).collect(); +// let all_bids = vec![vec![big_bid.clone()], small_bids.clone()].into_iter().flatten().collect_vec(); +// +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.mainnet_token_max_supply = total_allocation; +// project_metadata.total_allocation_size = total_allocation; +// project_metadata.auction_round_allocation_percentage = Percent::from_percent(100); +// +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()), +// all_bids, +// ); +// +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let all_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); +// +// let higher_than_wap_bids = all_bids.iter().filter(|bid| bid.original_ct_usd_price > wap).collect_vec(); +// assert_eq!(higher_than_wap_bids.len(), (max_bids_per_project - 1u32) as usize); +// } +// +// #[test] +// fn auction_oversubscription() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let auction_allocation = +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; +// let bucket_size = Percent::from_percent(10) * auction_allocation; +// let bids = vec![ +// (BIDDER_1, auction_allocation).into(), +// (BIDDER_2, bucket_size).into(), +// (BIDDER_3, bucket_size).into(), +// (BIDDER_4, bucket_size).into(), +// (BIDDER_5, bucket_size).into(), +// (BIDDER_6, bucket_size).into(), +// ]; +// +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// bids, +// ); +// +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// dbg!(wap); +// assert!(wap > project_metadata.minimum_price); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn contribute_does_not_work() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// let did = generate_did_from_account(ISSUER_1); +// let investor_type = InvestorType::Retail; +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::do_contribute( +// &BIDDER_1, +// project_id, +// 100, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// did, +// investor_type, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// Error::::IncorrectRound +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod start_auction_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn pallet_can_start_auction_automatically() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); +// let evaluations = default_evaluations(); +// let required_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// +// inst.mint_plmc_to(required_plmc); +// inst.evaluate_for_users(project_id, evaluations).unwrap(); +// +// let update_block = inst.get_update_block(project_id, &UpdateType::EvaluationEnd).unwrap(); +// inst.execute(|| System::set_block_number(update_block - 1)); +// inst.advance_time(1).unwrap(); +// +// let update_block = inst.get_update_block(project_id, &UpdateType::AuctionOpeningStart).unwrap(); +// inst.execute(|| System::set_block_number(update_block - 1)); +// inst.advance_time(1).unwrap(); +// } +// +// #[test] +// fn issuer_can_start_auction_manually() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); +// let evaluations = default_evaluations(); +// let required_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// inst.mint_plmc_to(required_plmc); +// inst.evaluate_for_users(project_id, evaluations).unwrap(); +// inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionInitializePeriod); +// inst.advance_time(1).unwrap(); +// inst.execute(|| Pallet::::do_start_auction(ISSUER_1, project_id)).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Auction); +// } +// +// #[test] +// fn stranger_cannot_start_auction_manually() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); +// let evaluations = default_evaluations(); +// let required_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// inst.mint_plmc_to(required_plmc); +// inst.evaluate_for_users(project_id, evaluations).unwrap(); +// inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionInitializePeriod); +// inst.advance_time(1).unwrap(); +// +// for account in 6000..6010 { +// inst.execute(|| { +// let response = Pallet::::do_start_auction(account, project_id); +// assert_noop!(response, Error::::NotIssuer); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn cannot_start_auction_manually_before_evaluation_finishes() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::do_start_auction(ISSUER_1, project_id), +// Error::::TransitionPointNotSet +// ); +// }); +// } +// +// #[test] +// fn cannot_start_auction_manually_if_evaluation_fails() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); +// inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::do_start_auction(ISSUER_1, project_id), +// Error::::TransitionPointNotSet +// ); +// }); +// assert_eq!( +// inst.get_project_details(project_id).status, +// ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) +// ); +// } +// +// #[test] +// fn auction_doesnt_start_automatically_if_evaluation_fails() { +// // Test our success assumption is ok +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); +// let evaluations = default_evaluations(); +// let required_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// inst.mint_plmc_to(required_plmc); +// inst.evaluate_for_users(project_id, evaluations).unwrap(); +// inst.start_auction(project_id, ISSUER_1).unwrap(); +// +// // Main test with failed evaluation +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1, None); +// +// let evaluation_end_execution = inst.get_update_block(project_id, &UpdateType::EvaluationEnd).unwrap(); +// inst.execute(|| System::set_block_number(evaluation_end_execution - 1)); +// inst.advance_time(1).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// } +// } +// } +// +// #[cfg(test)] +// mod bid_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use frame_support::{dispatch::DispatchResultWithPostInfo, traits::fungible::InspectFreeze}; +// +// #[test] +// fn evaluation_bond_counts_towards_bid() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let mut evaluations = default_evaluations(); +// let evaluator_bidder = 69; +// let evaluation_amount = 420 * USD_UNIT; +// let evaluator_bid = BidParams::new(evaluator_bidder, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// evaluations.push((evaluator_bidder, evaluation_amount).into()); +// +// let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); +// +// let already_bonded_plmc = inst +// .calculate_evaluation_plmc_spent(vec![(evaluator_bidder, evaluation_amount).into()], false)[0] +// .plmc_amount; +// +// let usable_evaluation_plmc = +// already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; +// +// let necessary_plmc_for_bid = inst.calculate_auction_plmc_charged_with_given_price( +// &vec![evaluator_bid.clone()], +// project_metadata.minimum_price, +// false, +// )[0] +// .plmc_amount; +// +// let necessary_usdt_for_bid = inst.calculate_auction_funding_asset_charged_with_given_price( +// &vec![evaluator_bid.clone()], +// project_metadata.minimum_price, +// ); +// +// inst.mint_plmc_to(vec![UserToPLMCBalance::new( +// evaluator_bidder, +// necessary_plmc_for_bid - usable_evaluation_plmc, +// )]); +// inst.mint_foreign_asset_to(necessary_usdt_for_bid); +// +// inst.bid_for_users(project_id, vec![evaluator_bid]).unwrap(); +// +// let evaluation_items = inst.execute(|| { +// Evaluations::::iter_prefix_values((project_id, evaluator_bidder)).collect_vec() +// }); +// assert_eq!(evaluation_items.len(), 1); +// assert_eq!(evaluation_items[0].current_plmc_bond, already_bonded_plmc - usable_evaluation_plmc); +// +// let bid_items = +// inst.execute(|| Bids::::iter_prefix_values((project_id, evaluator_bidder)).collect_vec()); +// assert_eq!(bid_items.len(), 1); +// assert_eq!(bid_items[0].plmc_bond, necessary_plmc_for_bid); +// +// inst.do_reserved_plmc_assertions( +// vec![UserToPLMCBalance::new(evaluator_bidder, necessary_plmc_for_bid)], +// HoldReason::Participation(project_id).into(), +// ); +// inst.do_reserved_plmc_assertions( +// vec![UserToPLMCBalance::new(evaluator_bidder, already_bonded_plmc - usable_evaluation_plmc)], +// HoldReason::Evaluation(project_id).into(), +// ); +// } +// +// #[test] +// fn bid_with_multiple_currencies() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let mut project_metadata_all = default_project_metadata(ISSUER_1); +// project_metadata_all.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] +// .try_into() +// .unwrap(); +// +// let mut project_metadata_usdt = default_project_metadata(ISSUER_2); +// project_metadata_usdt.participation_currencies = vec![AcceptedFundingAsset::USDT].try_into().unwrap(); +// +// let mut project_metadata_usdc = default_project_metadata(ISSUER_3); +// project_metadata_usdc.participation_currencies = vec![AcceptedFundingAsset::USDC].try_into().unwrap(); +// +// let mut project_metadata_dot = default_project_metadata(ISSUER_4); +// project_metadata_dot.participation_currencies = vec![AcceptedFundingAsset::DOT].try_into().unwrap(); +// +// let evaluations = default_evaluations(); +// +// let usdt_bid = BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let usdc_bid = BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); +// let dot_bid = BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); +// +// let plmc_fundings = inst.calculate_auction_plmc_charged_with_given_price( +// &vec![usdt_bid.clone(), usdc_bid.clone(), dot_bid.clone()], +// project_metadata_all.minimum_price, +// true, +// ); +// +// inst.mint_plmc_to(plmc_fundings.clone()); +// inst.mint_plmc_to(plmc_fundings.clone()); +// inst.mint_plmc_to(plmc_fundings.clone()); +// +// let usdt_fundings = inst.calculate_auction_funding_asset_charged_with_given_price( +// &vec![usdt_bid.clone(), usdc_bid.clone(), dot_bid.clone()], +// project_metadata_all.minimum_price, +// ); +// inst.mint_foreign_asset_to(usdt_fundings.clone()); +// inst.mint_foreign_asset_to(usdt_fundings.clone()); +// inst.mint_foreign_asset_to(usdt_fundings.clone()); +// +// let project_id_all = +// inst.create_auctioning_project(project_metadata_all, ISSUER_1, None, evaluations.clone()); +// assert_ok!(inst.bid_for_users(project_id_all, vec![usdt_bid.clone(), usdc_bid.clone(), dot_bid.clone()])); +// +// let project_id_usdt = +// inst.create_auctioning_project(project_metadata_usdt, ISSUER_2, None, evaluations.clone()); +// assert_ok!(inst.bid_for_users(project_id_usdt, vec![usdt_bid.clone()])); +// assert_err!( +// inst.bid_for_users(project_id_usdt, vec![usdc_bid.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_err!( +// inst.bid_for_users(project_id_usdt, vec![dot_bid.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// +// let project_id_usdc = +// inst.create_auctioning_project(project_metadata_usdc, ISSUER_3, None, evaluations.clone()); +// assert_err!( +// inst.bid_for_users(project_id_usdc, vec![usdt_bid.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_ok!(inst.bid_for_users(project_id_usdc, vec![usdc_bid.clone()])); +// assert_err!( +// inst.bid_for_users(project_id_usdc, vec![dot_bid.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// +// let project_id_dot = +// inst.create_auctioning_project(project_metadata_dot, ISSUER_4, None, evaluations.clone()); +// assert_err!( +// inst.bid_for_users(project_id_dot, vec![usdt_bid.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_err!( +// inst.bid_for_users(project_id_dot, vec![usdc_bid.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_ok!(inst.bid_for_users(project_id_dot, vec![dot_bid.clone()])); +// } +// +// fn test_bid_setup( +// inst: &mut MockInstantiator, +// project_id: ProjectId, +// bidder: AccountIdOf, +// investor_type: InvestorType, +// u8_multiplier: u8, +// ) -> DispatchResultWithPostInfo { +// let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); +// let jwt = get_mock_jwt_with_cid( +// bidder.clone(), +// investor_type, +// generate_did_from_account(BIDDER_1), +// project_policy, +// ); +// let amount = 1000 * CT_UNIT; +// let multiplier = Multiplier::force_new(u8_multiplier); +// +// if u8_multiplier > 0 { +// let bid = BidParams:: { +// bidder: bidder.clone(), +// amount, +// multiplier, +// asset: AcceptedFundingAsset::USDT, +// }; +// let min_price = inst.get_project_metadata(project_id).minimum_price; +// let necessary_plmc = +// inst.calculate_auction_plmc_charged_with_given_price(&vec![bid.clone()], min_price, true); +// let necessary_usdt = +// inst.calculate_auction_funding_asset_charged_with_given_price(&vec![bid.clone()], min_price); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// inst.mint_foreign_asset_to(necessary_usdt.clone()); +// } +// inst.execute(|| { +// Pallet::::bid( +// RuntimeOrigin::signed(bidder), +// jwt, +// project_id, +// amount, +// multiplier, +// AcceptedFundingAsset::USDT, +// ) +// }) +// } +// +// #[test] +// fn multiplier_limits() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); +// let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations); +// // Professional bids: 0x multiplier should fail +// assert_err!( +// test_bid_setup(&mut inst, project_id, BIDDER_1, InvestorType::Professional, 0), +// Error::::ForbiddenMultiplier +// ); +// // Professional bids: 1 - 10x multiplier should work +// for multiplier in 1..=10u8 { +// assert_ok!(test_bid_setup(&mut inst, project_id, BIDDER_1, InvestorType::Professional, multiplier)); +// } +// // Professional bids: >=11x multiplier should fail +// for multiplier in 11..=50u8 { +// assert_err!( +// test_bid_setup(&mut inst, project_id, BIDDER_1, InvestorType::Professional, multiplier), +// Error::::ForbiddenMultiplier +// ); +// } +// +// // Institutional bids: 0x multiplier should fail +// assert_err!( +// test_bid_setup(&mut inst, project_id, BIDDER_2, InvestorType::Institutional, 0), +// Error::::ForbiddenMultiplier +// ); +// // Institutional bids: 1 - 25x multiplier should work +// for multiplier in 1..=25u8 { +// assert_ok!(test_bid_setup(&mut inst, project_id, BIDDER_2, InvestorType::Institutional, multiplier)); +// } +// // Institutional bids: >=26x multiplier should fail +// for multiplier in 26..=50u8 { +// assert_err!( +// test_bid_setup(&mut inst, project_id, BIDDER_2, InvestorType::Institutional, multiplier), +// Error::::ForbiddenMultiplier +// ); +// } +// } +// +// #[test] +// fn bid_split_into_multiple_buckets() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(1.0), +// USD_DECIMALS, +// project_metadata.clone().token_information.decimals, +// ) +// .unwrap(); +// project_metadata.auction_round_allocation_percentage = Percent::from_percent(50u8); +// +// let evaluations = default_evaluations(); +// let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations); +// +// // bid that fills 80% of the first bucket +// let bid_40_percent = inst.generate_bids_from_total_ct_percent( +// project_metadata.clone(), +// 40u8, +// vec![100], +// vec![BIDDER_1], +// vec![8u8], +// ); +// +// // Note: 5% of total CTs is one bucket, i.e 10% of the auction allocation +// // This bid fills last 20% of the first bucket, +// // and gets split into 3 more bids of 2 more full and one partially full buckets. +// // 10% + 5% + 5% + 3% = 23% +// let bid_23_percent = inst.generate_bids_from_total_ct_percent( +// project_metadata.clone(), +// 23u8, +// vec![100], +// vec![BIDDER_2], +// vec![7u8], +// ); +// +// let all_bids = vec![bid_40_percent[0].clone(), bid_23_percent[0].clone()]; +// +// let necessary_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &all_bids, +// project_metadata.clone(), +// None, +// true, +// ); +// let necessary_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &all_bids, +// project_metadata.clone(), +// None, +// ); +// inst.mint_plmc_to(necessary_plmc.clone()); +// inst.mint_foreign_asset_to(necessary_usdt.clone()); +// +// inst.bid_for_users(project_id, bid_40_percent.clone()).unwrap(); +// let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); +// assert_eq!(stored_bids.len(), 1); +// +// inst.bid_for_users(project_id, bid_23_percent.clone()).unwrap(); +// let mut stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); +// stored_bids.sort_by(|a, b| a.id.cmp(&b.id)); +// // 40% + 10% + 5% + 5% + 3% = 5 total bids +// assert_eq!(stored_bids.len(), 5); +// +// let normalize_price = |decimal_aware_price| { +// PriceProviderOf::::convert_back_to_normal_price( +// decimal_aware_price, +// USD_DECIMALS, +// project_metadata.clone().token_information.decimals, +// ) +// .unwrap() +// }; +// assert_eq!(normalize_price(stored_bids[1].original_ct_usd_price), PriceOf::::from_float(1.0)); +// assert_eq!( +// stored_bids[1].original_ct_amount, +// Percent::from_percent(10) * project_metadata.total_allocation_size +// ); +// assert_eq!( +// normalize_price(stored_bids[2].original_ct_usd_price), +// PriceOf::::from_rational(11, 10) +// ); +// assert_eq!( +// stored_bids[2].original_ct_amount, +// Percent::from_percent(5) * project_metadata.total_allocation_size +// ); +// +// assert_eq!(normalize_price(stored_bids[3].original_ct_usd_price), PriceOf::::from_float(1.2)); +// assert_eq!( +// stored_bids[3].original_ct_amount, +// Percent::from_percent(5) * project_metadata.total_allocation_size +// ); +// +// assert_eq!(normalize_price(stored_bids[4].original_ct_usd_price), PriceOf::::from_float(1.3)); +// assert_eq!( +// stored_bids[4].original_ct_amount, +// Percent::from_percent(3) * project_metadata.total_allocation_size +// ); +// let current_bucket = inst.execute(|| Buckets::::get(project_id)).unwrap(); +// assert_eq!(normalize_price(current_bucket.current_price), PriceOf::::from_float(1.3)); +// assert_eq!(current_bucket.amount_left, Percent::from_percent(2) * project_metadata.total_allocation_size); +// assert_eq!(normalize_price(current_bucket.delta_price), PriceOf::::from_float(0.1)); +// } +// +// #[test] +// fn can_bid_with_frozen_tokens_funding_failed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), issuer, None, default_evaluations()); +// +// let bid = BidParams::new(BIDDER_4, 500 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let plmc_required = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &vec![bid.clone()], +// project_metadata.clone(), +// None, +// false, +// ); +// let frozen_amount = plmc_required[0].plmc_amount; +// let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); +// +// inst.mint_plmc_to(plmc_existential_deposits); +// inst.mint_plmc_to(plmc_required.clone()); +// +// inst.execute(|| { +// mock::Balances::set_freeze(&(), &BIDDER_4, plmc_required[0].plmc_amount).unwrap(); +// }); +// +// let usdt_required = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &vec![bid.clone()], +// project_metadata.clone(), +// None, +// ); +// inst.mint_foreign_asset_to(usdt_required); +// +// inst.execute(|| { +// assert_noop!( +// Balances::transfer_allow_death(RuntimeOrigin::signed(BIDDER_4), ISSUER_1, frozen_amount,), +// TokenError::Frozen +// ); +// }); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::bid( +// RuntimeOrigin::signed(BIDDER_4), +// get_mock_jwt_with_cid( +// BIDDER_4, +// InvestorType::Institutional, +// generate_did_from_account(BIDDER_4), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// bid.amount, +// bid.multiplier, +// bid.asset +// )); +// }); +// +// inst.start_community_funding(project_id).unwrap(); +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// inst.finish_funding(project_id, None).unwrap(); +// +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// +// let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// inst.execute(|| { +// PolimecFunding::settle_failed_bid(RuntimeOrigin::signed(BIDDER_4), project_id, BIDDER_4, 0).unwrap(); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Evaluation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); +// +// assert_eq!(free_balance, inst.get_ed() + frozen_amount); +// assert_eq!(bid_held_balance, Zero::zero()); +// assert_eq!(frozen_balance, frozen_amount); +// } +// +// #[test] +// fn can_bid_with_frozen_tokens_funding_success() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), issuer, None, default_evaluations()); +// +// let bid = BidParams::new(BIDDER_4, 500 * CT_UNIT, 5u8, AcceptedFundingAsset::USDT); +// let plmc_required = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &vec![bid.clone()], +// project_metadata.clone(), +// None, +// false, +// ); +// let frozen_amount = plmc_required[0].plmc_amount; +// let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); +// +// inst.mint_plmc_to(plmc_existential_deposits); +// inst.mint_plmc_to(plmc_required.clone()); +// +// inst.execute(|| { +// mock::Balances::set_freeze(&(), &BIDDER_4, plmc_required[0].plmc_amount).unwrap(); +// }); +// +// let usdt_required = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &vec![bid.clone()], +// project_metadata.clone(), +// None, +// ); +// inst.mint_foreign_asset_to(usdt_required); +// +// inst.execute(|| { +// assert_noop!( +// Balances::transfer_allow_death(RuntimeOrigin::signed(BIDDER_4), ISSUER_1, frozen_amount,), +// TokenError::Frozen +// ); +// }); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::bid( +// RuntimeOrigin::signed(BIDDER_4), +// get_mock_jwt_with_cid( +// BIDDER_4, +// InvestorType::Institutional, +// generate_did_from_account(BIDDER_4), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// bid.amount, +// bid.multiplier, +// bid.asset +// )); +// }); +// +// inst.start_community_funding(project_id).unwrap(); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let contributions = inst.generate_contributions_from_total_ct_percent( +// project_metadata.clone(), +// 90u8, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let plmc_required = inst.calculate_contributed_plmc_spent(contributions.clone(), wap, false); +// let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_required.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// let usdt_required = inst.calculate_contributed_funding_asset_spent(contributions.clone(), wap); +// inst.mint_foreign_asset_to(usdt_required.clone()); +// +// inst.contribute_for_users(project_id, contributions).unwrap(); +// +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// inst.finish_funding(project_id, None).unwrap(); +// +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// +// inst.execute(|| { +// PolimecFunding::settle_successful_bid(RuntimeOrigin::signed(BIDDER_4), project_id, BIDDER_4, 0) +// .unwrap(); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// +// let vest_duration = +// MultiplierOf::::new(5u8).unwrap().calculate_vesting_duration::(); +// let now = inst.current_block(); +// inst.jump_to_block(now + vest_duration + 1u64); +// inst.execute(|| { +// assert_ok!(mock::LinearRelease::vest( +// RuntimeOrigin::signed(BIDDER_4), +// HoldReason::Participation(project_id).into() +// )); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BIDDER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BIDDER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BIDDER_4)); +// +// assert_eq!(free_balance, inst.get_ed() + frozen_amount); +// assert_eq!(bid_held_balance, Zero::zero()); +// assert_eq!(frozen_balance, frozen_amount); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn cannot_use_all_of_evaluation_bond_on_bid() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let mut evaluations = default_evaluations(); +// let evaluator_bidder = 69; +// let evaluation_amount = 420 * USD_UNIT; +// let evaluator_bid = BidParams::new(evaluator_bidder, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// evaluations.push((evaluator_bidder, evaluation_amount).into()); +// +// let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); +// +// let necessary_usdt_for_bid = inst.calculate_auction_funding_asset_charged_with_given_price( +// &vec![evaluator_bid.clone()], +// project_metadata.minimum_price, +// ); +// +// inst.mint_foreign_asset_to(necessary_usdt_for_bid); +// +// assert_err!( +// inst.bid_for_users(project_id, vec![evaluator_bid]), +// Error::::ParticipantNotEnoughFunds +// ); +// } +// +// #[test] +// fn cannot_use_evaluation_bond_on_another_project_bid() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata_1 = default_project_metadata(ISSUER_1); +// let project_metadata_2 = default_project_metadata(ISSUER_2); +// +// let mut evaluations_1 = default_evaluations(); +// let evaluations_2 = default_evaluations(); +// +// let evaluator_bidder = 69; +// let evaluation_amount = 420 * USD_UNIT; +// let evaluator_bid = BidParams::new(evaluator_bidder, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// evaluations_1.push((evaluator_bidder, evaluation_amount).into()); +// +// let _project_id_1 = +// inst.create_auctioning_project(project_metadata_1.clone(), ISSUER_1, None, evaluations_1); +// let project_id_2 = +// inst.create_auctioning_project(project_metadata_2.clone(), ISSUER_2, None, evaluations_2); +// +// // Necessary Mints +// let already_bonded_plmc = inst +// .calculate_evaluation_plmc_spent(vec![(evaluator_bidder, evaluation_amount).into()], false)[0] +// .plmc_amount; +// let usable_evaluation_plmc = +// already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; +// let necessary_plmc_for_bid = inst.calculate_auction_plmc_charged_with_given_price( +// &vec![evaluator_bid.clone()], +// project_metadata_2.minimum_price, +// false, +// )[0] +// .plmc_amount; +// let necessary_usdt_for_bid = inst.calculate_auction_funding_asset_charged_with_given_price( +// &vec![evaluator_bid.clone()], +// project_metadata_2.minimum_price, +// ); +// inst.mint_plmc_to(vec![UserToPLMCBalance::new( +// evaluator_bidder, +// necessary_plmc_for_bid - usable_evaluation_plmc, +// )]); +// inst.mint_foreign_asset_to(necessary_usdt_for_bid); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::bid( +// RuntimeOrigin::signed(evaluator_bidder), +// get_mock_jwt_with_cid( +// evaluator_bidder, +// InvestorType::Professional, +// generate_did_from_account(evaluator_bidder), +// project_metadata_2.clone().policy_ipfs_cid.unwrap() +// ), +// project_id_2, +// evaluator_bid.amount, +// evaluator_bid.multiplier, +// evaluator_bid.asset +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// } +// +// #[test] +// fn cannot_bid_before_auction_round() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let _ = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// let did = generate_did_from_account(BIDDER_2); +// let investor_type = InvestorType::Institutional; +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::do_bid( +// &BIDDER_2, +// 0, +// 1, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// did, +// investor_type, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// Error::::IncorrectRound +// ); +// }); +// } +// +// #[test] +// fn cannot_bid_more_than_project_limit_count() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.mainnet_token_max_supply = 1_000_000_000 * CT_UNIT; +// project_metadata.total_allocation_size = 100_000_000 * CT_UNIT; +// +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), vec![EVALUATOR_1], vec![100u8]); +// let max_bids_per_project: u32 = ::MaxBidsPerProject::get(); +// let bids = +// (0u32..max_bids_per_project - 1).map(|i| (i as u32 + 420u32, 5000 * CT_UNIT).into()).collect_vec(); +// +// let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations); +// +// let plmc_for_bidding = inst.calculate_auction_plmc_charged_with_given_price( +// &bids.clone(), +// project_metadata.minimum_price, +// true, +// ); +// let usdt_for_bidding = inst.calculate_auction_funding_asset_charged_with_given_price( +// &bids.clone(), +// project_metadata.minimum_price, +// ); +// +// inst.mint_plmc_to(plmc_for_bidding.clone()); +// inst.mint_foreign_asset_to(usdt_for_bidding.clone()); +// inst.bid_for_users(project_id, bids.clone()).unwrap(); +// +// let current_bucket = inst.execute(|| Buckets::::get(project_id)).unwrap(); +// let remaining_ct = current_bucket.amount_left; +// +// // This bid should be split in 2, but the second one should fail, making the whole extrinsic fail and roll back storage +// let failing_bid = +// BidParams::::new(BIDDER_1, remaining_ct + 5000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let plmc_for_failing_bid = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &vec![failing_bid.clone()], +// project_metadata.clone(), +// Some(current_bucket), +// true, +// ); +// +// let usdt_for_bidding = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &vec![failing_bid.clone()], +// project_metadata.clone(), +// Some(current_bucket), +// ); +// +// inst.mint_plmc_to(plmc_for_failing_bid.clone()); +// inst.mint_foreign_asset_to(usdt_for_bidding.clone()); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::bid( +// RuntimeOrigin::signed(failing_bid.bidder), +// get_mock_jwt_with_cid( +// failing_bid.bidder, +// InvestorType::Professional, +// generate_did_from_account(failing_bid.bidder), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// failing_bid.amount, +// failing_bid.multiplier, +// failing_bid.asset +// ), +// Error::::TooManyProjectParticipations +// ); +// }); +// +// // Now we test that after reaching the limit, just one bid is also not allowed +// inst.execute(|| { +// assert_ok!(PolimecFunding::bid( +// RuntimeOrigin::signed(failing_bid.bidder), +// get_mock_jwt_with_cid( +// failing_bid.bidder, +// InvestorType::Professional, +// generate_did_from_account(failing_bid.bidder), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// remaining_ct, +// failing_bid.multiplier, +// failing_bid.asset +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::bid( +// RuntimeOrigin::signed(failing_bid.bidder), +// get_mock_jwt_with_cid( +// failing_bid.bidder, +// InvestorType::Professional, +// generate_did_from_account(failing_bid.bidder), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// 5000 * CT_UNIT, +// failing_bid.multiplier, +// failing_bid.asset +// ), +// Error::::TooManyProjectParticipations +// ); +// }); +// } +// +// #[test] +// fn cannot_bid_more_than_user_limit_count() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.mainnet_token_max_supply = 1_000_000_000 * CT_UNIT; +// project_metadata.total_allocation_size = 100_000_000 * CT_UNIT; +// +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), vec![EVALUATOR_1], vec![100u8]); +// let max_bids_per_user: u32 = ::MaxBidsPerUser::get(); +// let bids = (0u32..max_bids_per_user - 1u32).map(|_| (BIDDER_1, 5000 * CT_UNIT).into()).collect_vec(); +// +// let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations); +// +// let plmc_for_bidding = inst.calculate_auction_plmc_charged_with_given_price( +// &bids.clone(), +// project_metadata.minimum_price, +// true, +// ); +// let usdt_for_bidding = inst.calculate_auction_funding_asset_charged_with_given_price( +// &bids.clone(), +// project_metadata.minimum_price, +// ); +// +// inst.mint_plmc_to(plmc_for_bidding.clone()); +// inst.mint_foreign_asset_to(usdt_for_bidding.clone()); +// inst.bid_for_users(project_id, bids.clone()).unwrap(); +// +// let current_bucket = inst.execute(|| Buckets::::get(project_id)).unwrap(); +// let remaining_ct = current_bucket.amount_left; +// +// // This bid should be split in 2, but the second one should fail, making the whole extrinsic fail and roll back storage +// let failing_bid = +// BidParams::::new(BIDDER_1, remaining_ct + 5000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let plmc_for_failing_bid = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &vec![failing_bid.clone()], +// project_metadata.clone(), +// Some(current_bucket), +// true, +// ); +// let usdt_for_bidding = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &vec![failing_bid.clone()], +// project_metadata.clone(), +// Some(current_bucket), +// ); +// inst.mint_plmc_to(plmc_for_failing_bid.clone()); +// inst.mint_foreign_asset_to(usdt_for_bidding.clone()); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::bid( +// RuntimeOrigin::signed(failing_bid.bidder), +// get_mock_jwt_with_cid( +// failing_bid.bidder, +// InvestorType::Professional, +// generate_did_from_account(failing_bid.bidder), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// failing_bid.amount, +// failing_bid.multiplier, +// failing_bid.asset +// ), +// Error::::TooManyUserParticipations +// ); +// }); +// +// // Now we test that after reaching the limit, just one bid is also not allowed +// inst.execute(|| { +// assert_ok!(PolimecFunding::bid( +// RuntimeOrigin::signed(failing_bid.bidder), +// get_mock_jwt_with_cid( +// failing_bid.bidder, +// InvestorType::Professional, +// generate_did_from_account(failing_bid.bidder), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// remaining_ct, +// failing_bid.multiplier, +// failing_bid.asset +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::bid( +// RuntimeOrigin::signed(failing_bid.bidder), +// get_mock_jwt_with_cid( +// failing_bid.bidder, +// InvestorType::Professional, +// generate_did_from_account(failing_bid.bidder), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// 5000 * CT_UNIT, +// failing_bid.multiplier, +// failing_bid.asset +// ), +// Error::::TooManyUserParticipations +// ); +// }); +// } +// +// #[test] +// fn per_credential_type_ticket_size_minimums() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.total_allocation_size = 100_000 * CT_UNIT; +// project_metadata.bidding_ticket_sizes = BiddingTicketSizes { +// professional: TicketSize::new(8_000 * USD_UNIT, None), +// institutional: TicketSize::new(20_000 * USD_UNIT, None), +// phantom: Default::default(), +// }; +// +// let evaluations = default_evaluations(); +// +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations.clone()); +// +// inst.mint_plmc_to(vec![(BIDDER_1, 50_000 * CT_UNIT).into(), (BIDDER_2, 50_000 * CT_UNIT).into()]); +// +// inst.mint_foreign_asset_to(vec![ +// (BIDDER_1, 50_000 * USD_UNIT).into(), +// (BIDDER_2, 50_000 * USD_UNIT).into(), +// ]); +// +// // bid below 800 CT (8k USD) should fail for professionals +// inst.execute(|| { +// assert_noop!( +// Pallet::::do_bid( +// &BIDDER_1, +// project_id, +// 799 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(BIDDER_1), +// InvestorType::Professional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// Error::::TooLow +// ); +// }); +// // bid below 2000 CT (20k USD) should fail for institutionals +// inst.execute(|| { +// assert_noop!( +// Pallet::::do_bid( +// &BIDDER_2, +// project_id, +// 1999 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(BIDDER_1), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// Error::::TooLow +// ); +// }); +// } +// +// #[test] +// fn ticket_size_minimums_use_current_bucket_price() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.total_allocation_size = 100_000 * CT_UNIT; +// project_metadata.bidding_ticket_sizes = BiddingTicketSizes { +// professional: TicketSize::new(8_000 * USD_UNIT, None), +// institutional: TicketSize::new(20_000 * USD_UNIT, None), +// phantom: Default::default(), +// }; +// project_metadata.minimum_price = PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(1.0), +// USD_DECIMALS, +// project_metadata.clone().token_information.decimals, +// ) +// .unwrap(); +// +// let evaluations = default_evaluations(); +// +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations.clone()); +// +// inst.mint_plmc_to(vec![ +// (BIDDER_1, 200_000 * PLMC).into(), +// (BIDDER_2, 200_000 * PLMC).into(), +// (BIDDER_3, 200_000 * PLMC).into(), +// ]); +// inst.mint_foreign_asset_to(vec![ +// (BIDDER_1, 200_000 * USDT_UNIT).into(), +// (BIDDER_2, 200_000 * USDT_UNIT).into(), +// (BIDDER_3, 200_000 * USDT_UNIT).into(), +// ]); +// +// // First bucket is covered by one bidder +// let big_bid: BidParams = (BIDDER_1, 50_000 * CT_UNIT).into(); +// inst.bid_for_users(project_id, vec![big_bid.clone()]).unwrap(); +// +// // A bid at the min price of 1 should require a min of 8k CT, but with a new price of 1.1, we can now bid with less +// let bucket_increase_price = PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(1.1), +// USD_DECIMALS, +// project_metadata.clone().token_information.decimals, +// ) +// .unwrap(); +// let smallest_ct_amount_at_8k_usd = bucket_increase_price +// .reciprocal() +// .unwrap() +// .checked_mul_int(8000 * USD_UNIT) +// // add 1 because result could be .99999 of what we expect +// .unwrap() + 1; +// assert!(smallest_ct_amount_at_8k_usd < 8000 * CT_UNIT); +// inst.execute(|| { +// assert_ok!(Pallet::::do_bid( +// &BIDDER_1, +// project_id, +// smallest_ct_amount_at_8k_usd, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(BIDDER_1), +// InvestorType::Professional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )); +// }); +// let smallest_ct_amount_at_20k_usd = bucket_increase_price +// .reciprocal() +// .unwrap() +// .checked_mul_int(20_000 * USD_UNIT) +// // add 1 because result could be .99999 of what we expect +// .unwrap() + 1; +// assert!(smallest_ct_amount_at_20k_usd < 20_000 * CT_UNIT); +// inst.execute(|| { +// assert_ok!(Pallet::::do_bid( +// &BIDDER_2, +// project_id, +// smallest_ct_amount_at_20k_usd, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(BIDDER_1), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )); +// }); +// } +// +// #[test] +// fn per_credential_type_ticket_size_maximums() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.bidding_ticket_sizes = BiddingTicketSizes { +// professional: TicketSize::new(8_000 * USD_UNIT, Some(100_000 * USD_UNIT)), +// institutional: TicketSize::new(20_000 * USD_UNIT, Some(500_000 * USD_UNIT)), +// phantom: Default::default(), +// }; +// project_metadata.contributing_ticket_sizes = ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, Some(100_000 * USD_UNIT)), +// professional: TicketSize::new(USD_UNIT, Some(20_000 * USD_UNIT)), +// institutional: TicketSize::new(USD_UNIT, Some(50_000 * USD_UNIT)), +// phantom: Default::default(), +// }; +// let evaluations = default_evaluations(); +// +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, evaluations.clone()); +// +// inst.mint_plmc_to(vec![ +// (BIDDER_1, 500_000 * CT_UNIT).into(), +// (BIDDER_2, 500_000 * CT_UNIT).into(), +// (BIDDER_3, 500_000 * CT_UNIT).into(), +// (BIDDER_4, 500_000 * CT_UNIT).into(), +// ]); +// +// inst.mint_foreign_asset_to(vec![ +// (BIDDER_1, 500_000 * USD_UNIT).into(), +// (BIDDER_2, 500_000 * USD_UNIT).into(), +// (BIDDER_3, 500_000 * USD_UNIT).into(), +// (BIDDER_4, 500_000 * USD_UNIT).into(), +// ]); +// +// let bidder_1_jwt = get_mock_jwt_with_cid( +// BIDDER_1, +// InvestorType::Professional, +// generate_did_from_account(BIDDER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let bidder_2_jwt_same_did = get_mock_jwt_with_cid( +// BIDDER_2, +// InvestorType::Professional, +// generate_did_from_account(BIDDER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// // total bids with same DID above 10k CT (100k USD) should fail for professionals +// inst.execute(|| { +// assert_ok!(Pallet::::bid( +// RuntimeOrigin::signed(BIDDER_1), +// bidder_1_jwt, +// project_id, +// 8000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::bid( +// RuntimeOrigin::signed(BIDDER_2), +// bidder_2_jwt_same_did.clone(), +// project_id, +// 3000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT +// ), +// Error::::TooHigh +// ); +// }); +// // bidding 10k total works +// inst.execute(|| { +// assert_ok!(Pallet::::bid( +// RuntimeOrigin::signed(BIDDER_2), +// bidder_2_jwt_same_did, +// project_id, +// 2000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// +// let bidder_3_jwt = get_mock_jwt_with_cid( +// BIDDER_3, +// InvestorType::Institutional, +// generate_did_from_account(BIDDER_3), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let bidder_4_jwt_same_did = get_mock_jwt_with_cid( +// BIDDER_4, +// InvestorType::Institutional, +// generate_did_from_account(BIDDER_3), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// // total bids with same DID above 50k CT (500k USD) should fail for institutionals +// inst.execute(|| { +// assert_ok!(Pallet::::bid( +// RuntimeOrigin::signed(BIDDER_3), +// bidder_3_jwt, +// project_id, +// 40_000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::bid( +// RuntimeOrigin::signed(BIDDER_4), +// bidder_4_jwt_same_did.clone(), +// project_id, +// 11_000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooHigh +// ); +// }); +// // bidding 50k total works +// inst.execute(|| { +// assert_ok!(Pallet::::bid( +// RuntimeOrigin::signed(BIDDER_4), +// bidder_4_jwt_same_did, +// project_id, +// 10_000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// } +// +// #[test] +// fn issuer_cannot_bid_his_project() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); +// assert_err!( +// inst.execute(|| crate::Pallet::::do_bid( +// &(&ISSUER_1 + 1), +// project_id, +// 500 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(ISSUER_1), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )), +// Error::::ParticipationToOwnProject +// ); +// } +// +// #[test] +// fn bid_with_asset_not_accepted() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); +// let bids = vec![BidParams::::new(BIDDER_1, 10_000, 1u8, AcceptedFundingAsset::USDC)]; +// +// let did = generate_did_from_account(bids[0].bidder); +// let investor_type = InvestorType::Institutional; +// +// let outcome = inst.execute(|| { +// Pallet::::do_bid( +// &bids[0].bidder, +// project_id, +// bids[0].amount, +// bids[0].multiplier, +// bids[0].asset, +// did, +// investor_type, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ) +// }); +// frame_support::assert_err!(outcome, Error::::FundingAssetNotAccepted); +// } +// +// #[test] +// fn wrong_policy_on_jwt() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::bid( +// RuntimeOrigin::signed(BIDDER_1), +// get_mock_jwt_with_cid( +// BIDDER_1, +// InvestorType::Professional, +// generate_did_from_account(BIDDER_1), +// "wrong_cid".as_bytes().to_vec().try_into().unwrap() +// ), +// project_id, +// 5000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT +// ), +// Error::::PolicyMismatch +// ); +// }); +// } +// } +// } diff --git a/pallets/funding/src/tests/4_community.rs b/pallets/funding/src/tests/4_community.rs index b3ded28f6..0aa7dd876 100644 --- a/pallets/funding/src/tests/4_community.rs +++ b/pallets/funding/src/tests/4_community.rs @@ -1,1794 +1,1794 @@ -use super::*; - -#[cfg(test)] -mod round_flow { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use frame_support::traits::fungibles::metadata::Inspect; - use sp_runtime::bounded_vec; - use std::collections::HashSet; - - #[test] - fn community_round_completed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let _ = inst.create_remainder_contributing_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - ); - } - - #[test] - fn multiple_contribution_projects_completed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project1 = default_project_metadata(ISSUER_1); - let project2 = default_project_metadata(ISSUER_2); - let project3 = default_project_metadata(ISSUER_3); - let project4 = default_project_metadata(ISSUER_4); - let evaluations = default_evaluations(); - let bids = default_bids(); - let community_buys = default_community_buys(); - - inst.create_remainder_contributing_project( - project1, - ISSUER_1, - None, - evaluations.clone(), - bids.clone(), - community_buys.clone(), - ); - inst.create_remainder_contributing_project( - project2, - ISSUER_2, - None, - evaluations.clone(), - bids.clone(), - community_buys.clone(), - ); - inst.create_remainder_contributing_project( - project3, - ISSUER_3, - None, - evaluations.clone(), - bids.clone(), - community_buys.clone(), - ); - inst.create_remainder_contributing_project(project4, ISSUER_4, None, evaluations, bids, community_buys); - } - - #[test] - fn community_round_ends_on_all_ct_sold_exact() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let bids = vec![ - BidParams::new_with_defaults(BIDDER_1, 40_000 * CT_UNIT), - BidParams::new_with_defaults(BIDDER_2, 10_000 * CT_UNIT), - ]; - let project_id = inst.create_community_contributing_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - bids, - ); - const BOB: AccountId = 808; - - let remaining_ct = inst.get_project_details(project_id).remaining_contribution_tokens; - let ct_price = inst.get_project_details(project_id).weighted_average_price.expect("CT Price should exist"); - - let contributions = vec![ContributionParams::new(BOB, remaining_ct, 1u8, AcceptedFundingAsset::USDT)]; - let plmc_fundings = inst.calculate_contributed_plmc_spent(contributions.clone(), ct_price, false); - let plmc_existential_deposits = plmc_fundings.accounts().existential_deposits(); - let foreign_asset_fundings = - inst.calculate_contributed_funding_asset_spent(contributions.clone(), ct_price); - - inst.mint_plmc_to(plmc_fundings.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - inst.mint_foreign_asset_to(foreign_asset_fundings.clone()); - - // Buy remaining CTs - inst.contribute_for_users(project_id, contributions) - .expect("The Buyer should be able to buy the exact amount of remaining CTs"); - inst.advance_time(2u64).unwrap(); - // Check remaining CTs is 0 - assert_eq!( - inst.get_project_details(project_id).remaining_contribution_tokens, - 0, - "There are still remaining CTs" - ); - - // Check project is in FundingEnded state - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - - inst.do_free_plmc_assertions(plmc_existential_deposits); - inst.do_free_foreign_asset_assertions(vec![UserToForeignAssets::::new( - BOB, - 0_u128, - AcceptedFundingAsset::USDT.to_assethub_id(), - )]); - inst.do_reserved_plmc_assertions( - vec![plmc_fundings[0].clone()], - HoldReason::Participation(project_id).into(), - ); - inst.do_contribution_transferred_foreign_asset_assertions(foreign_asset_fundings, project_id); - } - - #[test] - fn round_has_total_ct_allocation_minus_auction_sold() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - ); - let project_details = inst.get_project_details(project_id); - let bid_ct_sold: BalanceOf = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .fold(Zero::zero(), |acc, bid| acc + bid.final_ct_amount) - }); - assert_eq!( - project_details.remaining_contribution_tokens, - project_metadata.total_allocation_size - bid_ct_sold - ); - - let contributions = vec![(BUYER_1, project_details.remaining_contribution_tokens).into()]; - - let plmc_contribution_funding = inst.calculate_contributed_plmc_spent( - contributions.clone(), - project_details.weighted_average_price.unwrap(), - false, - ); - let plmc_existential_deposits = plmc_contribution_funding.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_contribution_funding.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - let foreign_asset_contribution_funding = inst.calculate_contributed_funding_asset_spent( - contributions.clone(), - project_details.weighted_average_price.unwrap(), - ); - inst.mint_foreign_asset_to(foreign_asset_contribution_funding.clone()); - - inst.contribute_for_users(project_id, contributions).unwrap(); - - assert_eq!(inst.get_project_details(project_id).remaining_contribution_tokens, 0); - } - - #[test] - fn different_decimals_ct_works_as_expected() { - // Setup some base values to compare different decimals - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let ed = inst.get_ed(); - let default_project_metadata = default_project_metadata(ISSUER_1); - let original_decimal_aware_price = default_project_metadata.minimum_price; - let original_price = ::PriceProvider::convert_back_to_normal_price( - original_decimal_aware_price, - USD_DECIMALS, - default_project_metadata.token_information.decimals, - ) - .unwrap(); - let usable_plmc_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - PLMC_FOREIGN_ID, - USD_DECIMALS, - PLMC_DECIMALS, - ) - .unwrap() - }); - let usdt_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::USDT.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::USDT.to_assethub_id()), - ) - .unwrap() - }); - let usdc_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::USDC.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::USDC.to_assethub_id()), - ) - .unwrap() - }); - let dot_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::DOT.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::DOT.to_assethub_id()), - ) - .unwrap() - }); - - let mut funding_assets_cycle = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] - .into_iter() - .cycle(); - - let mut total_fundings_ct = Vec::new(); - let mut total_fundings_usd = Vec::new(); - let mut total_fundings_plmc = Vec::new(); - - let mut decimal_test = |decimals: u8| { - let funding_asset = funding_assets_cycle.next().unwrap(); - let funding_asset_usd_price = match funding_asset { - AcceptedFundingAsset::USDT => usdt_price, - AcceptedFundingAsset::USDC => usdc_price, - AcceptedFundingAsset::DOT => dot_price, - }; - - let mut project_metadata = default_project_metadata.clone(); - project_metadata.token_information.decimals = decimals; - project_metadata.minimum_price = - ::PriceProvider::calculate_decimals_aware_price( - original_price, - USD_DECIMALS, - decimals, - ) - .unwrap(); - - project_metadata.total_allocation_size = 1_000_000 * 10u128.pow(decimals as u32); - project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; - project_metadata.participation_currencies = bounded_vec!(funding_asset); - - let issuer: AccountIdOf = (10_000 + inst.get_new_nonce()).try_into().unwrap(); - let evaluations = inst.generate_successful_evaluations( - project_metadata.clone(), - default_evaluators(), - default_weights(), - ); - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - issuer, - None, - evaluations, - vec![], - ); - - let total_funding_ct = project_metadata.total_allocation_size; - let total_funding_usd = project_metadata.minimum_price.saturating_mul_int(total_funding_ct); - let total_funding_plmc = usable_plmc_price.reciprocal().unwrap().saturating_mul_int(total_funding_usd); - let total_funding_funding_asset = - funding_asset_usd_price.reciprocal().unwrap().saturating_mul_int(total_funding_usd); - - total_fundings_ct.push(total_funding_ct); - total_fundings_usd.push(total_funding_usd); - total_fundings_plmc.push(total_funding_plmc); - - // Every project should want to raise 10MM USD - assert_eq!(total_funding_usd, 10_000_000 * USD_UNIT); - - // Every project should produce the same PLMC bond when having the full funding at multiplier 1. - assert_close_enough!(total_funding_plmc, 1_190_476 * PLMC, Perquintill::from_float(0.999)); - - // Every project should have a different amount of CTs to raise, depending on their decimals - assert_eq!(total_funding_ct, 1_000_000 * 10u128.pow(decimals as u32)); - - // Buying all the remaining tokens. This is a fixed USD value, but the extrinsic amount depends on CT decimals. - inst.mint_plmc_to(vec![UserToPLMCBalance::new(BUYER_1, total_funding_plmc + ed)]); - inst.mint_foreign_asset_to(vec![UserToForeignAssets::new( - BUYER_1, - total_funding_funding_asset, - funding_asset.to_assethub_id(), - )]); - - assert_ok!(inst.execute(|| PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_1), - get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - total_funding_ct, - 1u8.try_into().unwrap(), - funding_asset, - ))); - - // the remaining tokens should be zero - assert_eq!(inst.get_project_details(project_id).remaining_contribution_tokens, 0); - - // We can successfully finish the project - inst.finish_funding(project_id, None).unwrap(); - }; - - for decimals in 6..=18 { - decimal_test(decimals); - } - - // Since we use the same original price and allocation size and adjust for decimals, - // the USD and PLMC amounts should be the same - assert!(total_fundings_usd.iter().all(|x| *x == total_fundings_usd[0])); - assert!(total_fundings_plmc.iter().all(|x| *x == total_fundings_plmc[0])); - - // CT amounts however should be different from each other - let mut hash_set_1 = HashSet::new(); - for amount in total_fundings_ct { - assert!(!hash_set_1.contains(&amount)); - hash_set_1.insert(amount); - } - } - } -} - -#[cfg(test)] -mod contribute_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use frame_support::{dispatch::DispatchResultWithPostInfo, traits::fungible::InspectFreeze}; - use polimec_common_test_utils::get_mock_jwt; - - #[test] - fn evaluation_bond_counts_towards_contribution() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - - const BOB: AccountId = 42069; - const CARL: AccountId = 420691; - let mut evaluations = default_evaluations(); - let bob_evaluation: UserToUSDBalance = (BOB, 1337 * USD_UNIT).into(); - let carl_evaluation: UserToUSDBalance = (CARL, 1337 * USD_UNIT).into(); - evaluations.push(bob_evaluation.clone()); - evaluations.push(carl_evaluation.clone()); - - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - evaluations, - default_bids(), - ); - let ct_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let plmc_price = ::PriceProvider::get_decimals_aware_price( - PLMC_FOREIGN_ID, - USD_DECIMALS, - PLMC_DECIMALS, - ) - .unwrap(); - - let evaluation_plmc_bond = - inst.execute(|| Balances::balance_on_hold(&HoldReason::Evaluation(project_id).into(), &BOB)); - let slashable_plmc = ::EvaluatorSlash::get() * evaluation_plmc_bond; - let usable_plmc = evaluation_plmc_bond - slashable_plmc; - - let usable_usd = plmc_price.checked_mul_int(usable_plmc).unwrap(); - let slashable_usd = plmc_price.checked_mul_int(slashable_plmc).unwrap(); - - let usable_ct = ct_price.reciprocal().unwrap().saturating_mul_int(usable_usd); - let slashable_ct = ct_price.reciprocal().unwrap().saturating_mul_int(slashable_usd); - - // Can't contribute with only the evaluation bond - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BOB), - get_mock_jwt_with_cid( - BOB, - InvestorType::Retail, - generate_did_from_account(BOB), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - usable_ct + slashable_ct, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::ParticipantNotEnoughFunds - ); - }); - - // Can partially use the usable evaluation bond (half in this case) - let contribution_usdt = - inst.calculate_contributed_funding_asset_spent(vec![(BOB, usable_ct / 2).into()], ct_price); - inst.mint_foreign_asset_to(contribution_usdt.clone()); - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(BOB), - get_mock_jwt_with_cid( - BOB, - InvestorType::Retail, - generate_did_from_account(BOB), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - usable_ct / 2, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - - // Can use the full evaluation bond - let contribution_usdt = - inst.calculate_contributed_funding_asset_spent(vec![(CARL, usable_ct).into()], ct_price); - inst.mint_foreign_asset_to(contribution_usdt.clone()); - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(CARL), - get_mock_jwt_with_cid( - CARL, - InvestorType::Retail, - generate_did_from_account(CARL), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - usable_ct, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - } - - #[test] - fn evaluation_bond_used_on_failed_bid_can_be_reused_on_contribution() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let bob = 42069; - let project_metadata = default_project_metadata(ISSUER_1); - // An evaluator that did a bid but it was not accepted at the end of the auction, can use that PLMC for contributing - let mut evaluations = default_evaluations(); - let bob_evaluation = (bob, 1337 * USD_UNIT).into(); - evaluations.push(bob_evaluation); - - let bids = default_bids(); - let bob_bid: BidParams = (bob, 1337 * CT_UNIT).into(); - let all_bids = bids.iter().chain(vec![bob_bid.clone()].iter()).cloned().collect_vec(); - - let project_id = - inst.create_auctioning_project(default_project_metadata(ISSUER_2), ISSUER_2, None, evaluations); - - let evaluation_plmc_bond = - inst.execute(|| Balances::balance_on_hold(&HoldReason::Evaluation(project_id).into(), &bob)); - let slashable_plmc_bond = ::EvaluatorSlash::get() * evaluation_plmc_bond; - let usable_plmc_bond = evaluation_plmc_bond - slashable_plmc_bond; - - let bids_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &all_bids, - project_metadata.clone(), - None, - true, - ); - inst.mint_plmc_to(bids_plmc.clone()); - - let bids_foreign = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &all_bids, - project_metadata.clone(), - None, - ); - inst.mint_foreign_asset_to(bids_foreign.clone()); - - inst.bid_for_users(project_id, bids).unwrap(); - - let auction_end = ::AuctionOpeningDuration::get() + - ::AuctionClosingDuration::get(); - inst.advance_time(auction_end - 1).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Auction); - inst.bid_for_users(project_id, vec![bob_bid]).unwrap(); - - inst.start_community_funding(project_id).unwrap(); - assert!(matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(..))); - - let plmc_price = ::PriceProvider::get_decimals_aware_price( - PLMC_FOREIGN_ID, - USD_DECIMALS, - PLMC_DECIMALS, - ) - .unwrap(); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let usable_usd = plmc_price.saturating_mul_int(usable_plmc_bond); - let usable_ct = wap.reciprocal().unwrap().saturating_mul_int(usable_usd); - - let bob_contribution = (bob, 1337 * CT_UNIT).into(); - let contribution_usdt = inst.calculate_contributed_funding_asset_spent(vec![bob_contribution], wap); - inst.mint_foreign_asset_to(contribution_usdt.clone()); - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(bob), - get_mock_jwt_with_cid( - bob, - InvestorType::Retail, - generate_did_from_account(bob), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - usable_ct, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - } - - #[test] - fn contribute_with_multiple_currencies() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata_all = default_project_metadata(ISSUER_1); - project_metadata_all.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] - .try_into() - .unwrap(); - - let project_id = inst.create_community_contributing_project( - project_metadata_all.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - ); - let usdt_contribution = ContributionParams::new(BUYER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let usdc_contribution = ContributionParams::new(BUYER_2, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); - let dot_contribution = ContributionParams::new(BUYER_3, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); - - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let plmc_fundings = inst.calculate_contributed_plmc_spent( - vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()], - wap, - false, - ); - let plmc_existential_deposits = plmc_fundings.accounts().existential_deposits(); - - let plmc_all_mints = - inst.generic_map_operation(vec![plmc_fundings, plmc_existential_deposits], MergeOperation::Add); - inst.mint_plmc_to(plmc_all_mints.clone()); - - let asset_hub_fundings = inst.calculate_contributed_funding_asset_spent( - vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()], - wap, - ); - inst.mint_foreign_asset_to(asset_hub_fundings.clone()); - - assert_ok!(inst.contribute_for_users( - project_id, - vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()] - )); - } - - fn test_contribution_setup( - inst: &mut MockInstantiator, - project_id: ProjectId, - contributor: AccountIdOf, - investor_type: InvestorType, - u8_multiplier: u8, - ) -> DispatchResultWithPostInfo { - let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); - let jwt = get_mock_jwt_with_cid( - contributor.clone(), - investor_type, - generate_did_from_account(contributor), - project_policy, - ); - let amount = 1000 * CT_UNIT; - let multiplier = Multiplier::force_new(u8_multiplier); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - if u8_multiplier > 0 { - let contribution = ContributionParams:: { - contributor: contributor.clone(), - amount, - multiplier, - asset: AcceptedFundingAsset::USDT, - }; - - let necessary_plmc = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let plmc_existential_amounts = necessary_plmc.accounts().existential_deposits(); - let necessary_usdt = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - - inst.mint_plmc_to(necessary_plmc.clone()); - inst.mint_plmc_to(plmc_existential_amounts.clone()); - inst.mint_foreign_asset_to(necessary_usdt.clone()); - } - inst.execute(|| { - Pallet::::contribute( - RuntimeOrigin::signed(contributor), - jwt, - project_id, - amount, - multiplier, - AcceptedFundingAsset::USDT, - ) - }) - } - - #[test] - fn non_retail_multiplier_limits() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.mainnet_token_max_supply = 80_000_000 * CT_UNIT; - project_metadata.total_allocation_size = 10_000_000 * CT_UNIT; - project_metadata.bidding_ticket_sizes = BiddingTicketSizes { - professional: TicketSize::new(5000 * USD_UNIT, None), - institutional: TicketSize::new(5000 * USD_UNIT, None), - phantom: Default::default(), - }; - project_metadata.contributing_ticket_sizes = ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, None), - professional: TicketSize::new(USD_UNIT, None), - institutional: TicketSize::new(USD_UNIT, None), - phantom: Default::default(), - }; - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); - let bids = inst.generate_bids_from_total_ct_percent( - project_metadata.clone(), - 50, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let project_id = - inst.create_community_contributing_project(project_metadata.clone(), ISSUER_1, None, evaluations, bids); - - // Professional contributions: 0x multiplier should fail - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Professional, 0), - Error::::ForbiddenMultiplier - ); - // Professional contributions: 1 - 10x multiplier should work - for multiplier in 1..=10u8 { - assert_ok!(test_contribution_setup( - &mut inst, - project_id, - BUYER_1, - InvestorType::Professional, - multiplier - )); - } - // Professional contributions: >=11x multiplier should fail - for multiplier in 11..=50u8 { - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Professional, multiplier), - Error::::ForbiddenMultiplier - ); - } - - // Institutional contributions: 0x multiplier should fail - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_2, InvestorType::Institutional, 0), - Error::::ForbiddenMultiplier - ); - // Institutional contributions: 1 - 25x multiplier should work - for multiplier in 1..=25u8 { - assert_ok!(test_contribution_setup( - &mut inst, - project_id, - BUYER_2, - InvestorType::Institutional, - multiplier - )); - } - // Institutional contributions: >=26x multiplier should fail - for multiplier in 26..=50u8 { - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_2, InvestorType::Institutional, multiplier), - Error::::ForbiddenMultiplier - ); - } - } - - #[test] - fn retail_multiplier_limits() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut issuer: AccountId = 6969420; - - let mut create_project = |inst: &mut MockInstantiator| { - issuer += 1; - inst.create_community_contributing_project( - default_project_metadata(issuer), - issuer, - None, - default_evaluations(), - default_bids(), - ) - }; - - let max_allowed_multipliers_map = vec![(2, 1), (4, 2), (9, 4), (24, 7), (25, 10)]; - - let mut previous_projects_created = 0; - for (projects_participated_amount, max_allowed_multiplier) in max_allowed_multipliers_map { - (previous_projects_created..projects_participated_amount - 1).for_each(|_| { - let project_id = create_project(&mut inst); - assert_ok!(test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, 1)); - }); - - let project_id = create_project(&mut inst); - previous_projects_created = projects_participated_amount; - - // 0x multiplier should fail - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, 0), - Error::::ForbiddenMultiplier - ); - - // Multipliers that should work - for multiplier in 1..=max_allowed_multiplier { - assert_ok!(test_contribution_setup( - &mut inst, - project_id, - BUYER_1, - InvestorType::Retail, - multiplier - )); - } - - // Multipliers that should NOT work - for multiplier in max_allowed_multiplier + 1..=50 { - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, multiplier), - Error::::ForbiddenMultiplier - ); - } - } - } - - #[test] - fn did_with_losing_bid_can_contribute() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let mut evaluations = default_evaluations(); - evaluations.push((BIDDER_4, 1337 * USD_UNIT).into()); - - let successful_bids = vec![ - BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_2, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ]; - let failing_bids_after_random_end = - vec![(BIDDER_3, 25_000 * CT_UNIT).into(), (BIDDER_4, 25_000 * CT_UNIT).into()]; - // This bids should fill the first bucket. - let failing_bids_sold_out = - vec![(BIDDER_5, 250_000 * CT_UNIT).into(), (BIDDER_6, 250_000 * CT_UNIT).into()]; - - let all_bids = failing_bids_sold_out - .iter() - .chain(successful_bids.iter()) - .chain(failing_bids_after_random_end.iter()) - .cloned() - .collect_vec(); - - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); - - let plmc_fundings = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &all_bids.clone(), - project_metadata.clone(), - None, - true, - ); - inst.mint_plmc_to(plmc_fundings.clone()); - - let foreign_funding = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &all_bids.clone(), - project_metadata.clone(), - None, - ); - inst.mint_foreign_asset_to(foreign_funding.clone()); - - inst.bid_for_users(project_id, failing_bids_sold_out).unwrap(); - inst.bid_for_users(project_id, successful_bids).unwrap(); - inst.advance_time( - ::AuctionOpeningDuration::get() + - ::AuctionClosingDuration::get() - - 1, - ) - .unwrap(); - inst.bid_for_users(project_id, failing_bids_after_random_end).unwrap(); - inst.start_community_funding(project_id).unwrap(); - - // Some low amount of plmc and usdt to cover a purchase of 10CTs. - let plmc_mints = vec![ - (BIDDER_3, 42069 * PLMC).into(), - (BIDDER_4, 42069 * PLMC).into(), - (BIDDER_5, 42069 * PLMC).into(), - (BIDDER_6, 42069 * PLMC).into(), - (BUYER_3, 42069 * PLMC).into(), - (BUYER_4, 42069 * PLMC).into(), - (BUYER_5, 42069 * PLMC).into(), - (BUYER_6, 42069 * PLMC).into(), - ]; - inst.mint_plmc_to(plmc_mints); - let usdt_mints = vec![ - (BIDDER_3, 42069 * CT_UNIT).into(), - (BIDDER_4, 42069 * CT_UNIT).into(), - (BIDDER_5, 42069 * CT_UNIT).into(), - (BIDDER_6, 42069 * CT_UNIT).into(), - (BUYER_3, 42069 * CT_UNIT).into(), - (BUYER_4, 42069 * CT_UNIT).into(), - (BUYER_5, 42069 * CT_UNIT).into(), - (BUYER_6, 42069 * CT_UNIT).into(), - ]; - inst.mint_foreign_asset_to(usdt_mints); - - let mut bid_should_succeed = |account, investor_type, did_acc| { - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(account), - get_mock_jwt_with_cid( - account, - investor_type, - generate_did_from_account(did_acc), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - 10 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - }; - - // Bidder 3 has a losing bid due to bidding after the random end. His did should be able to contribute regardless of what investor type - // or account he uses to sign the transaction - bid_should_succeed(BIDDER_3, InvestorType::Institutional, BIDDER_3); - bid_should_succeed(BUYER_3, InvestorType::Institutional, BIDDER_3); - bid_should_succeed(BIDDER_3, InvestorType::Professional, BIDDER_3); - bid_should_succeed(BUYER_3, InvestorType::Professional, BIDDER_3); - bid_should_succeed(BIDDER_3, InvestorType::Retail, BIDDER_3); - bid_should_succeed(BUYER_3, InvestorType::Retail, BIDDER_3); - - // Bidder 4 has a losing bid due to bidding after the random end, and he was also an evaluator. Same conditions as before should apply. - bid_should_succeed(BIDDER_4, InvestorType::Institutional, BIDDER_4); - bid_should_succeed(BUYER_4, InvestorType::Institutional, BIDDER_4); - bid_should_succeed(BIDDER_4, InvestorType::Professional, BIDDER_4); - bid_should_succeed(BUYER_4, InvestorType::Professional, BIDDER_4); - bid_should_succeed(BIDDER_4, InvestorType::Retail, BIDDER_4); - bid_should_succeed(BUYER_4, InvestorType::Retail, BIDDER_4); - - // Bidder 5 has a losing bid due to CTs being sold out at his price point. Same conditions as before should apply. - bid_should_succeed(BIDDER_5, InvestorType::Institutional, BIDDER_5); - bid_should_succeed(BUYER_5, InvestorType::Institutional, BIDDER_5); - bid_should_succeed(BIDDER_5, InvestorType::Professional, BIDDER_5); - bid_should_succeed(BUYER_5, InvestorType::Professional, BIDDER_5); - bid_should_succeed(BIDDER_5, InvestorType::Retail, BIDDER_5); - bid_should_succeed(BUYER_5, InvestorType::Retail, BIDDER_5); - - // Bidder 6 has a losing bid due to CTs being sold out at his price point, and he was also an evaluator. Same conditions as before should apply. - bid_should_succeed(BIDDER_6, InvestorType::Institutional, BIDDER_6); - bid_should_succeed(BUYER_6, InvestorType::Institutional, BIDDER_6); - bid_should_succeed(BIDDER_6, InvestorType::Professional, BIDDER_6); - bid_should_succeed(BUYER_6, InvestorType::Professional, BIDDER_6); - bid_should_succeed(BIDDER_6, InvestorType::Retail, BIDDER_6); - bid_should_succeed(BUYER_6, InvestorType::Retail, BIDDER_6); - } - - #[test] - fn can_contribute_with_frozen_tokens_funding_failed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - issuer, - None, - default_evaluations(), - vec![], - ); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let contribution = ContributionParams::new(BUYER_4, 500 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let frozen_amount = plmc_required[0].plmc_amount; - let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); - - inst.mint_plmc_to(plmc_existential_deposits); - inst.mint_plmc_to(plmc_required.clone()); - - inst.execute(|| { - mock::Balances::set_freeze(&(), &BUYER_4, plmc_required[0].plmc_amount).unwrap(); - }); - - let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - inst.mint_foreign_asset_to(usdt_required); - - inst.execute(|| { - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(BUYER_4), ISSUER_1, frozen_amount,), - TokenError::Frozen - ); - }); - - inst.execute(|| { - assert_ok!(PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_4), - get_mock_jwt_with_cid( - BUYER_4, - InvestorType::Institutional, - generate_did_from_account(BUYER_4), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - contribution.amount, - contribution.multiplier, - contribution.asset - )); - }); - - inst.start_remainder_or_end_funding(project_id).unwrap(); - inst.finish_funding(project_id, None).unwrap(); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - inst.execute(|| { - PolimecFunding::settle_failed_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0) - .unwrap(); - }); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Evaluation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - - assert_eq!(free_balance, inst.get_ed() + frozen_amount); - assert_eq!(bid_held_balance, Zero::zero()); - assert_eq!(frozen_balance, frozen_amount); - } - - #[test] - fn can_contribute_with_frozen_tokens_funding_success() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - issuer, - None, - default_evaluations(), - default_bids(), - ); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let contribution = ContributionParams::new(BUYER_4, 500 * CT_UNIT, 5u8, AcceptedFundingAsset::USDT); - let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let frozen_amount = plmc_required[0].plmc_amount; - let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); - - inst.mint_plmc_to(plmc_existential_deposits); - inst.mint_plmc_to(plmc_required.clone()); - - inst.execute(|| { - mock::Balances::set_freeze(&(), &BUYER_4, plmc_required[0].plmc_amount).unwrap(); - }); - - let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - inst.mint_foreign_asset_to(usdt_required); - - inst.execute(|| { - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(BUYER_4), ISSUER_1, frozen_amount,), - TokenError::Frozen - ); - }); - - inst.execute(|| { - assert_ok!(PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_4), - get_mock_jwt_with_cid( - BUYER_4, - InvestorType::Institutional, - generate_did_from_account(BUYER_4), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - contribution.amount, - contribution.multiplier, - contribution.asset - )); - }); - - inst.start_remainder_or_end_funding(project_id).unwrap(); - inst.finish_funding(project_id, None).unwrap(); - - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - - inst.execute(|| { - PolimecFunding::settle_successful_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0) - .unwrap(); - }); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - - let vest_duration = - MultiplierOf::::new(5u8).unwrap().calculate_vesting_duration::(); - let now = inst.current_block(); - inst.jump_to_block(now + vest_duration + 1u64); - inst.execute(|| { - assert_ok!(mock::LinearRelease::vest( - RuntimeOrigin::signed(BUYER_4), - HoldReason::Participation(project_id).into() - )); - }); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - - assert_eq!(free_balance, inst.get_ed() + frozen_amount); - assert_eq!(bid_held_balance, Zero::zero()); - assert_eq!(frozen_balance, frozen_amount); - } - } - - #[cfg(test)] - mod failure { - use super::*; - use frame_support::traits::{ - fungible::Mutate as MutateFungible, - fungibles::Mutate as MutateFungibles, - tokens::{Fortitude, Precision}, - }; - use sp_runtime::bounded_vec; - - #[test] - fn contribution_errors_if_user_limit_is_reached() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_community_contributing_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - ); - const CONTRIBUTOR: AccountIdOf = 420; - - let project_details = inst.get_project_details(project_id); - let token_price = project_details.weighted_average_price.unwrap(); - - // Create a contribution vector that will reach the limit of contributions for a user-project - let token_amount: BalanceOf = CT_UNIT; - let range = 0..::MaxContributionsPerUser::get(); - let contributions: Vec> = range - .map(|_| ContributionParams::new(CONTRIBUTOR, token_amount, 1u8, AcceptedFundingAsset::USDT)) - .collect(); - - let plmc_funding = inst.calculate_contributed_plmc_spent(contributions.clone(), token_price, false); - let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); - - let foreign_funding = inst.calculate_contributed_funding_asset_spent(contributions.clone(), token_price); - - inst.mint_plmc_to(plmc_funding.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - inst.mint_foreign_asset_to(foreign_funding.clone()); - - // Reach up to the limit of contributions for a user-project - assert!(inst.contribute_for_users(project_id, contributions).is_ok()); - - // Try to contribute again, but it should fail because the limit of contributions for a user-project was reached. - let over_limit_contribution = - ContributionParams::new(CONTRIBUTOR, token_amount, 1u8, AcceptedFundingAsset::USDT); - assert!(inst.contribute_for_users(project_id, vec![over_limit_contribution]).is_err()); - - // Check that the right amount of PLMC is bonded, and funding currency is transferred - let contributor_post_buy_plmc_balance = - inst.execute(|| ::NativeCurrency::balance(&CONTRIBUTOR)); - let contributor_post_buy_foreign_asset_balance = inst.execute(|| { - ::FundingCurrency::balance( - AcceptedFundingAsset::USDT.to_assethub_id(), - CONTRIBUTOR, - ) - }); - - assert_eq!(contributor_post_buy_plmc_balance, inst.get_ed()); - assert_eq!(contributor_post_buy_foreign_asset_balance, 0); - - let plmc_bond_stored = inst.execute(|| { - ::NativeCurrency::balance_on_hold( - &HoldReason::Participation(project_id.into()).into(), - &CONTRIBUTOR, - ) - }); - let foreign_asset_contributions_stored = inst.execute(|| { - Contributions::::iter_prefix_values((project_id, CONTRIBUTOR)) - .map(|c| c.funding_asset_amount) - .sum::>() - }); - - assert_eq!(plmc_bond_stored, inst.sum_balance_mappings(vec![plmc_funding.clone()])); - assert_eq!(foreign_asset_contributions_stored, inst.sum_foreign_mappings(vec![foreign_funding.clone()])); - } - - #[test] - fn issuer_cannot_contribute_his_project() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - ); - assert_err!( - inst.execute(|| crate::Pallet::::do_contribute( - &(&ISSUER_1 + 1), - project_id, - 500 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(ISSUER_1), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )), - Error::::ParticipationToOwnProject - ); - } - - #[test] - fn did_with_winning_bid_cannot_contribute() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let mut evaluations = default_evaluations(); - evaluations.push((BIDDER_2, 1337 * USD_UNIT).into()); - let bids = vec![ - BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_2, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - // Partially accepted bid. Only the 50k of the second bid will be accepted. - BidParams::new(BIDDER_3, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ]; - - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - bids, - ); - - let mut bid_should_fail = |account, investor_type, did_acc| { - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(account), - get_mock_jwt_with_cid( - account, - investor_type, - generate_did_from_account(did_acc), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - 10 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::UserHasWinningBid - ); - }); - }; - - // Bidder 1 has a winning bid, his did should not be able to contribute regardless of what investor type - // or account he uses to sign the transaction - bid_should_fail(BIDDER_1, InvestorType::Institutional, BIDDER_1); - bid_should_fail(BUYER_1, InvestorType::Institutional, BIDDER_1); - bid_should_fail(BIDDER_1, InvestorType::Professional, BIDDER_1); - bid_should_fail(BUYER_1, InvestorType::Professional, BIDDER_1); - bid_should_fail(BIDDER_1, InvestorType::Retail, BIDDER_1); - bid_should_fail(BUYER_1, InvestorType::Retail, BIDDER_1); - - // Bidder 2 has a winning bid, and he was also an evaluator. Same conditions as before should apply. - bid_should_fail(BIDDER_2, InvestorType::Institutional, BIDDER_2); - bid_should_fail(BUYER_2, InvestorType::Institutional, BIDDER_2); - bid_should_fail(BIDDER_2, InvestorType::Professional, BIDDER_2); - bid_should_fail(BUYER_2, InvestorType::Professional, BIDDER_2); - bid_should_fail(BIDDER_2, InvestorType::Retail, BIDDER_2); - bid_should_fail(BUYER_2, InvestorType::Retail, BIDDER_2); - - // Bidder 3 has a partial winning bid. Same conditions as before should apply. - bid_should_fail(BIDDER_3, InvestorType::Institutional, BIDDER_3); - bid_should_fail(BUYER_3, InvestorType::Institutional, BIDDER_3); - bid_should_fail(BIDDER_3, InvestorType::Professional, BIDDER_3); - bid_should_fail(BUYER_3, InvestorType::Professional, BIDDER_3); - bid_should_fail(BIDDER_3, InvestorType::Retail, BIDDER_3); - bid_should_fail(BUYER_3, InvestorType::Retail, BIDDER_3); - } - - #[test] - fn per_credential_type_ticket_size_minimums() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.contributing_ticket_sizes = ContributingTicketSizes { - retail: TicketSize::new(10 * USD_UNIT, None), - professional: TicketSize::new(100_000 * USD_UNIT, None), - institutional: TicketSize::new(200_000 * USD_UNIT, None), - phantom: Default::default(), - }; - - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - ); - - inst.mint_plmc_to(vec![ - (BUYER_1, 50_000 * CT_UNIT).into(), - (BUYER_2, 50_000 * CT_UNIT).into(), - (BUYER_3, 50_000 * CT_UNIT).into(), - ]); - - inst.mint_foreign_asset_to(vec![ - (BUYER_1, 50_000 * USD_UNIT).into(), - (BUYER_2, 50_000 * USD_UNIT).into(), - (BUYER_3, 50_000 * USD_UNIT).into(), - ]); - - // contribution below 1 CT (10 USD) should fail for retail - let jwt = get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_1), - jwt, - project_id, - CT_UNIT / 2, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooLow - ); - }); - // contribution below 10_000 CT (100k USD) should fail for professionals - let jwt = get_mock_jwt_with_cid( - BUYER_2, - InvestorType::Professional, - generate_did_from_account(BUYER_2), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_2), - jwt, - project_id, - 9_999, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooLow - ); - }); - - // contribution below 20_000 CT (200k USD) should fail for institutionals - let jwt = get_mock_jwt_with_cid( - BUYER_3, - InvestorType::Professional, - generate_did_from_account(BUYER_3), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_3), - jwt, - project_id, - 19_999, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooLow - ); - }); - } - - #[test] - fn per_credential_type_ticket_size_maximums() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.contributing_ticket_sizes = ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, Some(100_000 * USD_UNIT)), - professional: TicketSize::new(USD_UNIT, Some(20_000 * USD_UNIT)), - institutional: TicketSize::new(USD_UNIT, Some(50_000 * USD_UNIT)), - phantom: Default::default(), - }; - - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - ); - - inst.mint_plmc_to(vec![ - (BUYER_1, 500_000 * CT_UNIT).into(), - (BUYER_2, 500_000 * CT_UNIT).into(), - (BUYER_3, 500_000 * CT_UNIT).into(), - (BUYER_4, 500_000 * CT_UNIT).into(), - (BUYER_5, 500_000 * CT_UNIT).into(), - (BUYER_6, 500_000 * CT_UNIT).into(), - ]); - - inst.mint_foreign_asset_to(vec![ - (BUYER_1, 500_000 * USD_UNIT).into(), - (BUYER_2, 500_000 * USD_UNIT).into(), - (BUYER_3, 500_000 * USD_UNIT).into(), - (BUYER_4, 500_000 * USD_UNIT).into(), - (BUYER_5, 500_000 * USD_UNIT).into(), - (BUYER_6, 500_000 * USD_UNIT).into(), - ]); - - let buyer_1_jwt = get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let buyer_2_jwt_same_did = get_mock_jwt_with_cid( - BUYER_2, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - // total contributions with same DID above 10k CT (100k USD) should fail for retail - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(BUYER_1), - buyer_1_jwt, - project_id, - 9000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_2), - buyer_2_jwt_same_did.clone(), - project_id, - 1001 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooHigh - ); - }); - // bidding 2k total works - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(BUYER_2), - buyer_2_jwt_same_did, - project_id, - 1000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - - let buyer_3_jwt = get_mock_jwt_with_cid( - BUYER_3, - InvestorType::Professional, - generate_did_from_account(BUYER_3), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let buyer_4_jwt_same_did = get_mock_jwt_with_cid( - BUYER_4, - InvestorType::Professional, - generate_did_from_account(BUYER_3), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - // total contributions with same DID above 2k CT (20k USD) should fail for professionals - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(BUYER_3), - buyer_3_jwt, - project_id, - 1800 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_4), - buyer_4_jwt_same_did.clone(), - project_id, - 201 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooHigh - ); - }); - // bidding 2k total works - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(BUYER_4), - buyer_4_jwt_same_did, - project_id, - 200 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - - let buyer_5_jwt = get_mock_jwt_with_cid( - BUYER_5, - InvestorType::Institutional, - generate_did_from_account(BUYER_5), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let buyer_6_jwt_same_did = get_mock_jwt_with_cid( - BUYER_6, - InvestorType::Institutional, - generate_did_from_account(BUYER_5), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - // total contributions with same DID above 5k CT (50 USD) should fail for institutionals - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(BUYER_5), - buyer_5_jwt, - project_id, - 4690 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_6), - buyer_6_jwt_same_did.clone(), - project_id, - 311 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooHigh - ); - }); - // bidding 5k total works - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(BUYER_6), - buyer_6_jwt_same_did, - project_id, - 310 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - } - - #[test] - fn insufficient_funds() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - ); - - let jwt = get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let contribution = ContributionParams::new(BUYER_1, 1_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - // 1 unit less native asset than needed - let plmc_funding = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_funding.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - inst.execute(|| Balances::burn_from(&BUYER_1, 1, Precision::BestEffort, Fortitude::Force)).unwrap(); - - let foreign_funding = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - inst.mint_foreign_asset_to(foreign_funding.clone()); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_1), - jwt.clone(), - project_id, - 1_000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::ParticipantNotEnoughFunds - ); - }); - - // 1 unit less funding asset than needed - let plmc_funding = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_funding.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - let foreign_funding = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - - inst.execute(|| ForeignAssets::set_balance(AcceptedFundingAsset::USDT.to_assethub_id(), &BUYER_1, 0)); - inst.mint_foreign_asset_to(foreign_funding.clone()); - - inst.execute(|| { - ForeignAssets::burn_from( - AcceptedFundingAsset::USDT.to_assethub_id(), - &BUYER_1, - 100, - Precision::BestEffort, - Fortitude::Force, - ) - }) - .unwrap(); - - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_1), - jwt, - project_id, - 1_000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::ParticipantNotEnoughFunds - ); - }); - } - - #[test] - fn called_outside_community_round() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let created_project = inst.create_new_project(default_project_metadata(ISSUER_1), ISSUER_1, None); - let evaluating_project = inst.create_evaluating_project(default_project_metadata(ISSUER_2), ISSUER_2, None); - let auctioning_project = inst.create_auctioning_project( - default_project_metadata(ISSUER_3), - ISSUER_3, - None, - default_evaluations(), - ); - let remaining_project = inst.create_remainder_contributing_project( - default_project_metadata(ISSUER_4), - ISSUER_4, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - ); - let finished_project = inst.create_finished_project( - default_project_metadata(ISSUER_5), - ISSUER_5, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - - let projects = - vec![created_project, evaluating_project, auctioning_project, remaining_project, finished_project]; - for project in projects { - let project_policy = inst.get_project_metadata(project).policy_ipfs_cid.unwrap(); - inst.execute(|| { - assert_noop!( - PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_1), - get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_policy - ), - project, - 1000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT - ), - Error::::IncorrectRound - ); - }); - } - } - - #[test] - fn contribute_with_unaccepted_currencies() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let mut project_metadata_usdt = default_project_metadata(ISSUER_2); - project_metadata_usdt.participation_currencies = bounded_vec![AcceptedFundingAsset::USDT]; - - let mut project_metadata_usdc = default_project_metadata(ISSUER_3); - project_metadata_usdc.participation_currencies = bounded_vec![AcceptedFundingAsset::USDC]; - - let evaluations = default_evaluations(); - - let usdt_bids = default_bids() - .into_iter() - .map(|mut b| { - b.asset = AcceptedFundingAsset::USDT; - b - }) - .collect::>(); - - let usdc_bids = default_bids() - .into_iter() - .map(|mut b| { - b.asset = AcceptedFundingAsset::USDC; - b - }) - .collect::>(); - - let usdt_contribution = ContributionParams::new(BUYER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let usdc_contribution = ContributionParams::new(BUYER_2, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); - let dot_contribution = ContributionParams::new(BUYER_3, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); - - let project_id_usdc = inst.create_community_contributing_project( - project_metadata_usdc, - ISSUER_2, - None, - evaluations.clone(), - usdc_bids.clone(), - ); - assert_err!( - inst.contribute_for_users(project_id_usdc, vec![usdt_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - - let project_id_usdt = inst.create_community_contributing_project( - project_metadata_usdt, - ISSUER_3, - None, - evaluations.clone(), - usdt_bids.clone(), - ); - assert_err!( - inst.contribute_for_users(project_id_usdt, vec![usdc_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_err!( - inst.contribute_for_users(project_id_usdt, vec![dot_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - } - - #[test] - fn cannot_use_evaluation_bond_on_another_project_contribution() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata_1 = default_project_metadata(ISSUER_1); - let project_metadata_2 = default_project_metadata(ISSUER_2); - - let mut evaluations_1 = default_evaluations(); - let evaluations_2 = default_evaluations(); - - let evaluator_contributor = 69; - let evaluation_amount = 420 * USD_UNIT; - let evaluator_contribution = - ContributionParams::new(evaluator_contributor, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - evaluations_1.push((evaluator_contributor, evaluation_amount).into()); - - let _project_id_1 = inst.create_community_contributing_project( - project_metadata_1.clone(), - ISSUER_1, - None, - evaluations_1, - default_bids(), - ); - let project_id_2 = inst.create_community_contributing_project( - project_metadata_2.clone(), - ISSUER_2, - None, - evaluations_2, - default_bids(), - ); - - let wap = inst.get_project_details(project_id_2).weighted_average_price.unwrap(); - - // Necessary Mints - let already_bonded_plmc = inst - .calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount).into()], false)[0] - .plmc_amount; - let usable_evaluation_plmc = - already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; - let necessary_plmc_for_contribution = - inst.calculate_contributed_plmc_spent(vec![evaluator_contribution.clone()], wap, false)[0].plmc_amount; - let necessary_usdt_for_contribution = - inst.calculate_contributed_funding_asset_spent(vec![evaluator_contribution.clone()], wap); - inst.mint_plmc_to(vec![UserToPLMCBalance::new( - evaluator_contributor, - necessary_plmc_for_contribution - usable_evaluation_plmc, - )]); - inst.mint_foreign_asset_to(necessary_usdt_for_contribution); - - inst.execute(|| { - assert_noop!( - PolimecFunding::contribute( - RuntimeOrigin::signed(evaluator_contributor), - get_mock_jwt_with_cid( - evaluator_contributor, - InvestorType::Retail, - generate_did_from_account(evaluator_contributor), - project_metadata_2.clone().policy_ipfs_cid.unwrap() - ), - project_id_2, - evaluator_contribution.amount, - evaluator_contribution.multiplier, - evaluator_contribution.asset - ), - Error::::ParticipantNotEnoughFunds - ); - }); - } - - #[test] - fn wrong_policy_on_jwt() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - ); - - inst.execute(|| { - assert_noop!( - PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_1), - get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - "wrong_cid".as_bytes().to_vec().try_into().unwrap() - ), - project_id, - 5000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT - ), - Error::::PolicyMismatch - ); - }); - } - } -} +// use super::*; +// +// #[cfg(test)] +// mod round_flow { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use frame_support::traits::fungibles::metadata::Inspect; +// use sp_runtime::bounded_vec; +// use std::collections::HashSet; +// +// #[test] +// fn community_round_completed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let _ = inst.create_remainder_contributing_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// ); +// } +// +// #[test] +// fn multiple_contribution_projects_completed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project1 = default_project_metadata(ISSUER_1); +// let project2 = default_project_metadata(ISSUER_2); +// let project3 = default_project_metadata(ISSUER_3); +// let project4 = default_project_metadata(ISSUER_4); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// let community_buys = default_community_buys(); +// +// inst.create_remainder_contributing_project( +// project1, +// ISSUER_1, +// None, +// evaluations.clone(), +// bids.clone(), +// community_buys.clone(), +// ); +// inst.create_remainder_contributing_project( +// project2, +// ISSUER_2, +// None, +// evaluations.clone(), +// bids.clone(), +// community_buys.clone(), +// ); +// inst.create_remainder_contributing_project( +// project3, +// ISSUER_3, +// None, +// evaluations.clone(), +// bids.clone(), +// community_buys.clone(), +// ); +// inst.create_remainder_contributing_project(project4, ISSUER_4, None, evaluations, bids, community_buys); +// } +// +// #[test] +// fn community_round_ends_on_all_ct_sold_exact() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let bids = vec![ +// BidParams::new_with_defaults(BIDDER_1, 40_000 * CT_UNIT), +// BidParams::new_with_defaults(BIDDER_2, 10_000 * CT_UNIT), +// ]; +// let project_id = inst.create_community_contributing_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// bids, +// ); +// const BOB: AccountId = 808; +// +// let remaining_ct = inst.get_project_details(project_id).remaining_contribution_tokens; +// let ct_price = inst.get_project_details(project_id).weighted_average_price.expect("CT Price should exist"); +// +// let contributions = vec![ContributionParams::new(BOB, remaining_ct, 1u8, AcceptedFundingAsset::USDT)]; +// let plmc_fundings = inst.calculate_contributed_plmc_spent(contributions.clone(), ct_price, false); +// let plmc_existential_deposits = plmc_fundings.accounts().existential_deposits(); +// let foreign_asset_fundings = +// inst.calculate_contributed_funding_asset_spent(contributions.clone(), ct_price); +// +// inst.mint_plmc_to(plmc_fundings.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// inst.mint_foreign_asset_to(foreign_asset_fundings.clone()); +// +// // Buy remaining CTs +// inst.contribute_for_users(project_id, contributions) +// .expect("The Buyer should be able to buy the exact amount of remaining CTs"); +// inst.advance_time(2u64).unwrap(); +// // Check remaining CTs is 0 +// assert_eq!( +// inst.get_project_details(project_id).remaining_contribution_tokens, +// 0, +// "There are still remaining CTs" +// ); +// +// // Check project is in FundingEnded state +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// +// inst.do_free_plmc_assertions(plmc_existential_deposits); +// inst.do_free_foreign_asset_assertions(vec![UserToForeignAssets::::new( +// BOB, +// 0_u128, +// AcceptedFundingAsset::USDT.to_assethub_id(), +// )]); +// inst.do_reserved_plmc_assertions( +// vec![plmc_fundings[0].clone()], +// HoldReason::Participation(project_id).into(), +// ); +// inst.do_contribution_transferred_foreign_asset_assertions(foreign_asset_fundings, project_id); +// } +// +// #[test] +// fn round_has_total_ct_allocation_minus_auction_sold() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// ); +// let project_details = inst.get_project_details(project_id); +// let bid_ct_sold: BalanceOf = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .fold(Zero::zero(), |acc, bid| acc + bid.final_ct_amount) +// }); +// assert_eq!( +// project_details.remaining_contribution_tokens, +// project_metadata.total_allocation_size - bid_ct_sold +// ); +// +// let contributions = vec![(BUYER_1, project_details.remaining_contribution_tokens).into()]; +// +// let plmc_contribution_funding = inst.calculate_contributed_plmc_spent( +// contributions.clone(), +// project_details.weighted_average_price.unwrap(), +// false, +// ); +// let plmc_existential_deposits = plmc_contribution_funding.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_contribution_funding.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// let foreign_asset_contribution_funding = inst.calculate_contributed_funding_asset_spent( +// contributions.clone(), +// project_details.weighted_average_price.unwrap(), +// ); +// inst.mint_foreign_asset_to(foreign_asset_contribution_funding.clone()); +// +// inst.contribute_for_users(project_id, contributions).unwrap(); +// +// assert_eq!(inst.get_project_details(project_id).remaining_contribution_tokens, 0); +// } +// +// #[test] +// fn different_decimals_ct_works_as_expected() { +// // Setup some base values to compare different decimals +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let ed = inst.get_ed(); +// let default_project_metadata = default_project_metadata(ISSUER_1); +// let original_decimal_aware_price = default_project_metadata.minimum_price; +// let original_price = ::PriceProvider::convert_back_to_normal_price( +// original_decimal_aware_price, +// USD_DECIMALS, +// default_project_metadata.token_information.decimals, +// ) +// .unwrap(); +// let usable_plmc_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// PLMC_FOREIGN_ID, +// USD_DECIMALS, +// PLMC_DECIMALS, +// ) +// .unwrap() +// }); +// let usdt_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::USDT.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::USDT.to_assethub_id()), +// ) +// .unwrap() +// }); +// let usdc_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::USDC.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::USDC.to_assethub_id()), +// ) +// .unwrap() +// }); +// let dot_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::DOT.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::DOT.to_assethub_id()), +// ) +// .unwrap() +// }); +// +// let mut funding_assets_cycle = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] +// .into_iter() +// .cycle(); +// +// let mut total_fundings_ct = Vec::new(); +// let mut total_fundings_usd = Vec::new(); +// let mut total_fundings_plmc = Vec::new(); +// +// let mut decimal_test = |decimals: u8| { +// let funding_asset = funding_assets_cycle.next().unwrap(); +// let funding_asset_usd_price = match funding_asset { +// AcceptedFundingAsset::USDT => usdt_price, +// AcceptedFundingAsset::USDC => usdc_price, +// AcceptedFundingAsset::DOT => dot_price, +// }; +// +// let mut project_metadata = default_project_metadata.clone(); +// project_metadata.token_information.decimals = decimals; +// project_metadata.minimum_price = +// ::PriceProvider::calculate_decimals_aware_price( +// original_price, +// USD_DECIMALS, +// decimals, +// ) +// .unwrap(); +// +// project_metadata.total_allocation_size = 1_000_000 * 10u128.pow(decimals as u32); +// project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; +// project_metadata.participation_currencies = bounded_vec!(funding_asset); +// +// let issuer: AccountIdOf = (10_000 + inst.get_new_nonce()).try_into().unwrap(); +// let evaluations = inst.generate_successful_evaluations( +// project_metadata.clone(), +// default_evaluators(), +// default_weights(), +// ); +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// issuer, +// None, +// evaluations, +// vec![], +// ); +// +// let total_funding_ct = project_metadata.total_allocation_size; +// let total_funding_usd = project_metadata.minimum_price.saturating_mul_int(total_funding_ct); +// let total_funding_plmc = usable_plmc_price.reciprocal().unwrap().saturating_mul_int(total_funding_usd); +// let total_funding_funding_asset = +// funding_asset_usd_price.reciprocal().unwrap().saturating_mul_int(total_funding_usd); +// +// total_fundings_ct.push(total_funding_ct); +// total_fundings_usd.push(total_funding_usd); +// total_fundings_plmc.push(total_funding_plmc); +// +// // Every project should want to raise 10MM USD +// assert_eq!(total_funding_usd, 10_000_000 * USD_UNIT); +// +// // Every project should produce the same PLMC bond when having the full funding at multiplier 1. +// assert_close_enough!(total_funding_plmc, 1_190_476 * PLMC, Perquintill::from_float(0.999)); +// +// // Every project should have a different amount of CTs to raise, depending on their decimals +// assert_eq!(total_funding_ct, 1_000_000 * 10u128.pow(decimals as u32)); +// +// // Buying all the remaining tokens. This is a fixed USD value, but the extrinsic amount depends on CT decimals. +// inst.mint_plmc_to(vec![UserToPLMCBalance::new(BUYER_1, total_funding_plmc + ed)]); +// inst.mint_foreign_asset_to(vec![UserToForeignAssets::new( +// BUYER_1, +// total_funding_funding_asset, +// funding_asset.to_assethub_id(), +// )]); +// +// assert_ok!(inst.execute(|| PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_1), +// get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// total_funding_ct, +// 1u8.try_into().unwrap(), +// funding_asset, +// ))); +// +// // the remaining tokens should be zero +// assert_eq!(inst.get_project_details(project_id).remaining_contribution_tokens, 0); +// +// // We can successfully finish the project +// inst.finish_funding(project_id, None).unwrap(); +// }; +// +// for decimals in 6..=18 { +// decimal_test(decimals); +// } +// +// // Since we use the same original price and allocation size and adjust for decimals, +// // the USD and PLMC amounts should be the same +// assert!(total_fundings_usd.iter().all(|x| *x == total_fundings_usd[0])); +// assert!(total_fundings_plmc.iter().all(|x| *x == total_fundings_plmc[0])); +// +// // CT amounts however should be different from each other +// let mut hash_set_1 = HashSet::new(); +// for amount in total_fundings_ct { +// assert!(!hash_set_1.contains(&amount)); +// hash_set_1.insert(amount); +// } +// } +// } +// } +// +// #[cfg(test)] +// mod contribute_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use frame_support::{dispatch::DispatchResultWithPostInfo, traits::fungible::InspectFreeze}; +// use polimec_common_test_utils::get_mock_jwt; +// +// #[test] +// fn evaluation_bond_counts_towards_contribution() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// +// const BOB: AccountId = 42069; +// const CARL: AccountId = 420691; +// let mut evaluations = default_evaluations(); +// let bob_evaluation: UserToUSDBalance = (BOB, 1337 * USD_UNIT).into(); +// let carl_evaluation: UserToUSDBalance = (CARL, 1337 * USD_UNIT).into(); +// evaluations.push(bob_evaluation.clone()); +// evaluations.push(carl_evaluation.clone()); +// +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// evaluations, +// default_bids(), +// ); +// let ct_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let plmc_price = ::PriceProvider::get_decimals_aware_price( +// PLMC_FOREIGN_ID, +// USD_DECIMALS, +// PLMC_DECIMALS, +// ) +// .unwrap(); +// +// let evaluation_plmc_bond = +// inst.execute(|| Balances::balance_on_hold(&HoldReason::Evaluation(project_id).into(), &BOB)); +// let slashable_plmc = ::EvaluatorSlash::get() * evaluation_plmc_bond; +// let usable_plmc = evaluation_plmc_bond - slashable_plmc; +// +// let usable_usd = plmc_price.checked_mul_int(usable_plmc).unwrap(); +// let slashable_usd = plmc_price.checked_mul_int(slashable_plmc).unwrap(); +// +// let usable_ct = ct_price.reciprocal().unwrap().saturating_mul_int(usable_usd); +// let slashable_ct = ct_price.reciprocal().unwrap().saturating_mul_int(slashable_usd); +// +// // Can't contribute with only the evaluation bond +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BOB), +// get_mock_jwt_with_cid( +// BOB, +// InvestorType::Retail, +// generate_did_from_account(BOB), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// usable_ct + slashable_ct, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// +// // Can partially use the usable evaluation bond (half in this case) +// let contribution_usdt = +// inst.calculate_contributed_funding_asset_spent(vec![(BOB, usable_ct / 2).into()], ct_price); +// inst.mint_foreign_asset_to(contribution_usdt.clone()); +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(BOB), +// get_mock_jwt_with_cid( +// BOB, +// InvestorType::Retail, +// generate_did_from_account(BOB), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// usable_ct / 2, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// +// // Can use the full evaluation bond +// let contribution_usdt = +// inst.calculate_contributed_funding_asset_spent(vec![(CARL, usable_ct).into()], ct_price); +// inst.mint_foreign_asset_to(contribution_usdt.clone()); +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(CARL), +// get_mock_jwt_with_cid( +// CARL, +// InvestorType::Retail, +// generate_did_from_account(CARL), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// usable_ct, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// } +// +// #[test] +// fn evaluation_bond_used_on_failed_bid_can_be_reused_on_contribution() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let bob = 42069; +// let project_metadata = default_project_metadata(ISSUER_1); +// // An evaluator that did a bid but it was not accepted at the end of the auction, can use that PLMC for contributing +// let mut evaluations = default_evaluations(); +// let bob_evaluation = (bob, 1337 * USD_UNIT).into(); +// evaluations.push(bob_evaluation); +// +// let bids = default_bids(); +// let bob_bid: BidParams = (bob, 1337 * CT_UNIT).into(); +// let all_bids = bids.iter().chain(vec![bob_bid.clone()].iter()).cloned().collect_vec(); +// +// let project_id = +// inst.create_auctioning_project(default_project_metadata(ISSUER_2), ISSUER_2, None, evaluations); +// +// let evaluation_plmc_bond = +// inst.execute(|| Balances::balance_on_hold(&HoldReason::Evaluation(project_id).into(), &bob)); +// let slashable_plmc_bond = ::EvaluatorSlash::get() * evaluation_plmc_bond; +// let usable_plmc_bond = evaluation_plmc_bond - slashable_plmc_bond; +// +// let bids_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &all_bids, +// project_metadata.clone(), +// None, +// true, +// ); +// inst.mint_plmc_to(bids_plmc.clone()); +// +// let bids_foreign = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &all_bids, +// project_metadata.clone(), +// None, +// ); +// inst.mint_foreign_asset_to(bids_foreign.clone()); +// +// inst.bid_for_users(project_id, bids).unwrap(); +// +// let auction_end = ::AuctionOpeningDuration::get() + +// ::AuctionClosingDuration::get(); +// inst.advance_time(auction_end - 1).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Auction); +// inst.bid_for_users(project_id, vec![bob_bid]).unwrap(); +// +// inst.start_community_funding(project_id).unwrap(); +// assert!(matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(..))); +// +// let plmc_price = ::PriceProvider::get_decimals_aware_price( +// PLMC_FOREIGN_ID, +// USD_DECIMALS, +// PLMC_DECIMALS, +// ) +// .unwrap(); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let usable_usd = plmc_price.saturating_mul_int(usable_plmc_bond); +// let usable_ct = wap.reciprocal().unwrap().saturating_mul_int(usable_usd); +// +// let bob_contribution = (bob, 1337 * CT_UNIT).into(); +// let contribution_usdt = inst.calculate_contributed_funding_asset_spent(vec![bob_contribution], wap); +// inst.mint_foreign_asset_to(contribution_usdt.clone()); +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(bob), +// get_mock_jwt_with_cid( +// bob, +// InvestorType::Retail, +// generate_did_from_account(bob), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// usable_ct, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// } +// +// #[test] +// fn contribute_with_multiple_currencies() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata_all = default_project_metadata(ISSUER_1); +// project_metadata_all.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] +// .try_into() +// .unwrap(); +// +// let project_id = inst.create_community_contributing_project( +// project_metadata_all.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// ); +// let usdt_contribution = ContributionParams::new(BUYER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let usdc_contribution = ContributionParams::new(BUYER_2, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); +// let dot_contribution = ContributionParams::new(BUYER_3, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); +// +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let plmc_fundings = inst.calculate_contributed_plmc_spent( +// vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()], +// wap, +// false, +// ); +// let plmc_existential_deposits = plmc_fundings.accounts().existential_deposits(); +// +// let plmc_all_mints = +// inst.generic_map_operation(vec![plmc_fundings, plmc_existential_deposits], MergeOperation::Add); +// inst.mint_plmc_to(plmc_all_mints.clone()); +// +// let asset_hub_fundings = inst.calculate_contributed_funding_asset_spent( +// vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()], +// wap, +// ); +// inst.mint_foreign_asset_to(asset_hub_fundings.clone()); +// +// assert_ok!(inst.contribute_for_users( +// project_id, +// vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()] +// )); +// } +// +// fn test_contribution_setup( +// inst: &mut MockInstantiator, +// project_id: ProjectId, +// contributor: AccountIdOf, +// investor_type: InvestorType, +// u8_multiplier: u8, +// ) -> DispatchResultWithPostInfo { +// let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); +// let jwt = get_mock_jwt_with_cid( +// contributor.clone(), +// investor_type, +// generate_did_from_account(contributor), +// project_policy, +// ); +// let amount = 1000 * CT_UNIT; +// let multiplier = Multiplier::force_new(u8_multiplier); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// if u8_multiplier > 0 { +// let contribution = ContributionParams:: { +// contributor: contributor.clone(), +// amount, +// multiplier, +// asset: AcceptedFundingAsset::USDT, +// }; +// +// let necessary_plmc = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let plmc_existential_amounts = necessary_plmc.accounts().existential_deposits(); +// let necessary_usdt = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// inst.mint_plmc_to(plmc_existential_amounts.clone()); +// inst.mint_foreign_asset_to(necessary_usdt.clone()); +// } +// inst.execute(|| { +// Pallet::::contribute( +// RuntimeOrigin::signed(contributor), +// jwt, +// project_id, +// amount, +// multiplier, +// AcceptedFundingAsset::USDT, +// ) +// }) +// } +// +// #[test] +// fn non_retail_multiplier_limits() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.mainnet_token_max_supply = 80_000_000 * CT_UNIT; +// project_metadata.total_allocation_size = 10_000_000 * CT_UNIT; +// project_metadata.bidding_ticket_sizes = BiddingTicketSizes { +// professional: TicketSize::new(5000 * USD_UNIT, None), +// institutional: TicketSize::new(5000 * USD_UNIT, None), +// phantom: Default::default(), +// }; +// project_metadata.contributing_ticket_sizes = ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, None), +// professional: TicketSize::new(USD_UNIT, None), +// institutional: TicketSize::new(USD_UNIT, None), +// phantom: Default::default(), +// }; +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); +// let bids = inst.generate_bids_from_total_ct_percent( +// project_metadata.clone(), +// 50, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let project_id = +// inst.create_community_contributing_project(project_metadata.clone(), ISSUER_1, None, evaluations, bids); +// +// // Professional contributions: 0x multiplier should fail +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Professional, 0), +// Error::::ForbiddenMultiplier +// ); +// // Professional contributions: 1 - 10x multiplier should work +// for multiplier in 1..=10u8 { +// assert_ok!(test_contribution_setup( +// &mut inst, +// project_id, +// BUYER_1, +// InvestorType::Professional, +// multiplier +// )); +// } +// // Professional contributions: >=11x multiplier should fail +// for multiplier in 11..=50u8 { +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Professional, multiplier), +// Error::::ForbiddenMultiplier +// ); +// } +// +// // Institutional contributions: 0x multiplier should fail +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_2, InvestorType::Institutional, 0), +// Error::::ForbiddenMultiplier +// ); +// // Institutional contributions: 1 - 25x multiplier should work +// for multiplier in 1..=25u8 { +// assert_ok!(test_contribution_setup( +// &mut inst, +// project_id, +// BUYER_2, +// InvestorType::Institutional, +// multiplier +// )); +// } +// // Institutional contributions: >=26x multiplier should fail +// for multiplier in 26..=50u8 { +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_2, InvestorType::Institutional, multiplier), +// Error::::ForbiddenMultiplier +// ); +// } +// } +// +// #[test] +// fn retail_multiplier_limits() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut issuer: AccountId = 6969420; +// +// let mut create_project = |inst: &mut MockInstantiator| { +// issuer += 1; +// inst.create_community_contributing_project( +// default_project_metadata(issuer), +// issuer, +// None, +// default_evaluations(), +// default_bids(), +// ) +// }; +// +// let max_allowed_multipliers_map = vec![(2, 1), (4, 2), (9, 4), (24, 7), (25, 10)]; +// +// let mut previous_projects_created = 0; +// for (projects_participated_amount, max_allowed_multiplier) in max_allowed_multipliers_map { +// (previous_projects_created..projects_participated_amount - 1).for_each(|_| { +// let project_id = create_project(&mut inst); +// assert_ok!(test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, 1)); +// }); +// +// let project_id = create_project(&mut inst); +// previous_projects_created = projects_participated_amount; +// +// // 0x multiplier should fail +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, 0), +// Error::::ForbiddenMultiplier +// ); +// +// // Multipliers that should work +// for multiplier in 1..=max_allowed_multiplier { +// assert_ok!(test_contribution_setup( +// &mut inst, +// project_id, +// BUYER_1, +// InvestorType::Retail, +// multiplier +// )); +// } +// +// // Multipliers that should NOT work +// for multiplier in max_allowed_multiplier + 1..=50 { +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, multiplier), +// Error::::ForbiddenMultiplier +// ); +// } +// } +// } +// +// #[test] +// fn did_with_losing_bid_can_contribute() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let mut evaluations = default_evaluations(); +// evaluations.push((BIDDER_4, 1337 * USD_UNIT).into()); +// +// let successful_bids = vec![ +// BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_2, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ]; +// let failing_bids_after_random_end = +// vec![(BIDDER_3, 25_000 * CT_UNIT).into(), (BIDDER_4, 25_000 * CT_UNIT).into()]; +// // This bids should fill the first bucket. +// let failing_bids_sold_out = +// vec![(BIDDER_5, 250_000 * CT_UNIT).into(), (BIDDER_6, 250_000 * CT_UNIT).into()]; +// +// let all_bids = failing_bids_sold_out +// .iter() +// .chain(successful_bids.iter()) +// .chain(failing_bids_after_random_end.iter()) +// .cloned() +// .collect_vec(); +// +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); +// +// let plmc_fundings = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &all_bids.clone(), +// project_metadata.clone(), +// None, +// true, +// ); +// inst.mint_plmc_to(plmc_fundings.clone()); +// +// let foreign_funding = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &all_bids.clone(), +// project_metadata.clone(), +// None, +// ); +// inst.mint_foreign_asset_to(foreign_funding.clone()); +// +// inst.bid_for_users(project_id, failing_bids_sold_out).unwrap(); +// inst.bid_for_users(project_id, successful_bids).unwrap(); +// inst.advance_time( +// ::AuctionOpeningDuration::get() + +// ::AuctionClosingDuration::get() - +// 1, +// ) +// .unwrap(); +// inst.bid_for_users(project_id, failing_bids_after_random_end).unwrap(); +// inst.start_community_funding(project_id).unwrap(); +// +// // Some low amount of plmc and usdt to cover a purchase of 10CTs. +// let plmc_mints = vec![ +// (BIDDER_3, 42069 * PLMC).into(), +// (BIDDER_4, 42069 * PLMC).into(), +// (BIDDER_5, 42069 * PLMC).into(), +// (BIDDER_6, 42069 * PLMC).into(), +// (BUYER_3, 42069 * PLMC).into(), +// (BUYER_4, 42069 * PLMC).into(), +// (BUYER_5, 42069 * PLMC).into(), +// (BUYER_6, 42069 * PLMC).into(), +// ]; +// inst.mint_plmc_to(plmc_mints); +// let usdt_mints = vec![ +// (BIDDER_3, 42069 * CT_UNIT).into(), +// (BIDDER_4, 42069 * CT_UNIT).into(), +// (BIDDER_5, 42069 * CT_UNIT).into(), +// (BIDDER_6, 42069 * CT_UNIT).into(), +// (BUYER_3, 42069 * CT_UNIT).into(), +// (BUYER_4, 42069 * CT_UNIT).into(), +// (BUYER_5, 42069 * CT_UNIT).into(), +// (BUYER_6, 42069 * CT_UNIT).into(), +// ]; +// inst.mint_foreign_asset_to(usdt_mints); +// +// let mut bid_should_succeed = |account, investor_type, did_acc| { +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(account), +// get_mock_jwt_with_cid( +// account, +// investor_type, +// generate_did_from_account(did_acc), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// 10 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// }; +// +// // Bidder 3 has a losing bid due to bidding after the random end. His did should be able to contribute regardless of what investor type +// // or account he uses to sign the transaction +// bid_should_succeed(BIDDER_3, InvestorType::Institutional, BIDDER_3); +// bid_should_succeed(BUYER_3, InvestorType::Institutional, BIDDER_3); +// bid_should_succeed(BIDDER_3, InvestorType::Professional, BIDDER_3); +// bid_should_succeed(BUYER_3, InvestorType::Professional, BIDDER_3); +// bid_should_succeed(BIDDER_3, InvestorType::Retail, BIDDER_3); +// bid_should_succeed(BUYER_3, InvestorType::Retail, BIDDER_3); +// +// // Bidder 4 has a losing bid due to bidding after the random end, and he was also an evaluator. Same conditions as before should apply. +// bid_should_succeed(BIDDER_4, InvestorType::Institutional, BIDDER_4); +// bid_should_succeed(BUYER_4, InvestorType::Institutional, BIDDER_4); +// bid_should_succeed(BIDDER_4, InvestorType::Professional, BIDDER_4); +// bid_should_succeed(BUYER_4, InvestorType::Professional, BIDDER_4); +// bid_should_succeed(BIDDER_4, InvestorType::Retail, BIDDER_4); +// bid_should_succeed(BUYER_4, InvestorType::Retail, BIDDER_4); +// +// // Bidder 5 has a losing bid due to CTs being sold out at his price point. Same conditions as before should apply. +// bid_should_succeed(BIDDER_5, InvestorType::Institutional, BIDDER_5); +// bid_should_succeed(BUYER_5, InvestorType::Institutional, BIDDER_5); +// bid_should_succeed(BIDDER_5, InvestorType::Professional, BIDDER_5); +// bid_should_succeed(BUYER_5, InvestorType::Professional, BIDDER_5); +// bid_should_succeed(BIDDER_5, InvestorType::Retail, BIDDER_5); +// bid_should_succeed(BUYER_5, InvestorType::Retail, BIDDER_5); +// +// // Bidder 6 has a losing bid due to CTs being sold out at his price point, and he was also an evaluator. Same conditions as before should apply. +// bid_should_succeed(BIDDER_6, InvestorType::Institutional, BIDDER_6); +// bid_should_succeed(BUYER_6, InvestorType::Institutional, BIDDER_6); +// bid_should_succeed(BIDDER_6, InvestorType::Professional, BIDDER_6); +// bid_should_succeed(BUYER_6, InvestorType::Professional, BIDDER_6); +// bid_should_succeed(BIDDER_6, InvestorType::Retail, BIDDER_6); +// bid_should_succeed(BUYER_6, InvestorType::Retail, BIDDER_6); +// } +// +// #[test] +// fn can_contribute_with_frozen_tokens_funding_failed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// issuer, +// None, +// default_evaluations(), +// vec![], +// ); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let contribution = ContributionParams::new(BUYER_4, 500 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let frozen_amount = plmc_required[0].plmc_amount; +// let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); +// +// inst.mint_plmc_to(plmc_existential_deposits); +// inst.mint_plmc_to(plmc_required.clone()); +// +// inst.execute(|| { +// mock::Balances::set_freeze(&(), &BUYER_4, plmc_required[0].plmc_amount).unwrap(); +// }); +// +// let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// inst.mint_foreign_asset_to(usdt_required); +// +// inst.execute(|| { +// assert_noop!( +// Balances::transfer_allow_death(RuntimeOrigin::signed(BUYER_4), ISSUER_1, frozen_amount,), +// TokenError::Frozen +// ); +// }); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_4), +// get_mock_jwt_with_cid( +// BUYER_4, +// InvestorType::Institutional, +// generate_did_from_account(BUYER_4), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// contribution.amount, +// contribution.multiplier, +// contribution.asset +// )); +// }); +// +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// inst.finish_funding(project_id, None).unwrap(); +// +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// inst.execute(|| { +// PolimecFunding::settle_failed_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0) +// .unwrap(); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Evaluation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// +// assert_eq!(free_balance, inst.get_ed() + frozen_amount); +// assert_eq!(bid_held_balance, Zero::zero()); +// assert_eq!(frozen_balance, frozen_amount); +// } +// +// #[test] +// fn can_contribute_with_frozen_tokens_funding_success() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// issuer, +// None, +// default_evaluations(), +// default_bids(), +// ); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let contribution = ContributionParams::new(BUYER_4, 500 * CT_UNIT, 5u8, AcceptedFundingAsset::USDT); +// let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let frozen_amount = plmc_required[0].plmc_amount; +// let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); +// +// inst.mint_plmc_to(plmc_existential_deposits); +// inst.mint_plmc_to(plmc_required.clone()); +// +// inst.execute(|| { +// mock::Balances::set_freeze(&(), &BUYER_4, plmc_required[0].plmc_amount).unwrap(); +// }); +// +// let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// inst.mint_foreign_asset_to(usdt_required); +// +// inst.execute(|| { +// assert_noop!( +// Balances::transfer_allow_death(RuntimeOrigin::signed(BUYER_4), ISSUER_1, frozen_amount,), +// TokenError::Frozen +// ); +// }); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_4), +// get_mock_jwt_with_cid( +// BUYER_4, +// InvestorType::Institutional, +// generate_did_from_account(BUYER_4), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// contribution.amount, +// contribution.multiplier, +// contribution.asset +// )); +// }); +// +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// inst.finish_funding(project_id, None).unwrap(); +// +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// +// inst.execute(|| { +// PolimecFunding::settle_successful_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0) +// .unwrap(); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// +// let vest_duration = +// MultiplierOf::::new(5u8).unwrap().calculate_vesting_duration::(); +// let now = inst.current_block(); +// inst.jump_to_block(now + vest_duration + 1u64); +// inst.execute(|| { +// assert_ok!(mock::LinearRelease::vest( +// RuntimeOrigin::signed(BUYER_4), +// HoldReason::Participation(project_id).into() +// )); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// +// assert_eq!(free_balance, inst.get_ed() + frozen_amount); +// assert_eq!(bid_held_balance, Zero::zero()); +// assert_eq!(frozen_balance, frozen_amount); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// use frame_support::traits::{ +// fungible::Mutate as MutateFungible, +// fungibles::Mutate as MutateFungibles, +// tokens::{Fortitude, Precision}, +// }; +// use sp_runtime::bounded_vec; +// +// #[test] +// fn contribution_errors_if_user_limit_is_reached() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_community_contributing_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// ); +// const CONTRIBUTOR: AccountIdOf = 420; +// +// let project_details = inst.get_project_details(project_id); +// let token_price = project_details.weighted_average_price.unwrap(); +// +// // Create a contribution vector that will reach the limit of contributions for a user-project +// let token_amount: BalanceOf = CT_UNIT; +// let range = 0..::MaxContributionsPerUser::get(); +// let contributions: Vec> = range +// .map(|_| ContributionParams::new(CONTRIBUTOR, token_amount, 1u8, AcceptedFundingAsset::USDT)) +// .collect(); +// +// let plmc_funding = inst.calculate_contributed_plmc_spent(contributions.clone(), token_price, false); +// let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); +// +// let foreign_funding = inst.calculate_contributed_funding_asset_spent(contributions.clone(), token_price); +// +// inst.mint_plmc_to(plmc_funding.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// inst.mint_foreign_asset_to(foreign_funding.clone()); +// +// // Reach up to the limit of contributions for a user-project +// assert!(inst.contribute_for_users(project_id, contributions).is_ok()); +// +// // Try to contribute again, but it should fail because the limit of contributions for a user-project was reached. +// let over_limit_contribution = +// ContributionParams::new(CONTRIBUTOR, token_amount, 1u8, AcceptedFundingAsset::USDT); +// assert!(inst.contribute_for_users(project_id, vec![over_limit_contribution]).is_err()); +// +// // Check that the right amount of PLMC is bonded, and funding currency is transferred +// let contributor_post_buy_plmc_balance = +// inst.execute(|| ::NativeCurrency::balance(&CONTRIBUTOR)); +// let contributor_post_buy_foreign_asset_balance = inst.execute(|| { +// ::FundingCurrency::balance( +// AcceptedFundingAsset::USDT.to_assethub_id(), +// CONTRIBUTOR, +// ) +// }); +// +// assert_eq!(contributor_post_buy_plmc_balance, inst.get_ed()); +// assert_eq!(contributor_post_buy_foreign_asset_balance, 0); +// +// let plmc_bond_stored = inst.execute(|| { +// ::NativeCurrency::balance_on_hold( +// &HoldReason::Participation(project_id.into()).into(), +// &CONTRIBUTOR, +// ) +// }); +// let foreign_asset_contributions_stored = inst.execute(|| { +// Contributions::::iter_prefix_values((project_id, CONTRIBUTOR)) +// .map(|c| c.funding_asset_amount) +// .sum::>() +// }); +// +// assert_eq!(plmc_bond_stored, inst.sum_balance_mappings(vec![plmc_funding.clone()])); +// assert_eq!(foreign_asset_contributions_stored, inst.sum_foreign_mappings(vec![foreign_funding.clone()])); +// } +// +// #[test] +// fn issuer_cannot_contribute_his_project() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// ); +// assert_err!( +// inst.execute(|| crate::Pallet::::do_contribute( +// &(&ISSUER_1 + 1), +// project_id, +// 500 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(ISSUER_1), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )), +// Error::::ParticipationToOwnProject +// ); +// } +// +// #[test] +// fn did_with_winning_bid_cannot_contribute() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let mut evaluations = default_evaluations(); +// evaluations.push((BIDDER_2, 1337 * USD_UNIT).into()); +// let bids = vec![ +// BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_2, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// // Partially accepted bid. Only the 50k of the second bid will be accepted. +// BidParams::new(BIDDER_3, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ]; +// +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// bids, +// ); +// +// let mut bid_should_fail = |account, investor_type, did_acc| { +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(account), +// get_mock_jwt_with_cid( +// account, +// investor_type, +// generate_did_from_account(did_acc), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// 10 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::UserHasWinningBid +// ); +// }); +// }; +// +// // Bidder 1 has a winning bid, his did should not be able to contribute regardless of what investor type +// // or account he uses to sign the transaction +// bid_should_fail(BIDDER_1, InvestorType::Institutional, BIDDER_1); +// bid_should_fail(BUYER_1, InvestorType::Institutional, BIDDER_1); +// bid_should_fail(BIDDER_1, InvestorType::Professional, BIDDER_1); +// bid_should_fail(BUYER_1, InvestorType::Professional, BIDDER_1); +// bid_should_fail(BIDDER_1, InvestorType::Retail, BIDDER_1); +// bid_should_fail(BUYER_1, InvestorType::Retail, BIDDER_1); +// +// // Bidder 2 has a winning bid, and he was also an evaluator. Same conditions as before should apply. +// bid_should_fail(BIDDER_2, InvestorType::Institutional, BIDDER_2); +// bid_should_fail(BUYER_2, InvestorType::Institutional, BIDDER_2); +// bid_should_fail(BIDDER_2, InvestorType::Professional, BIDDER_2); +// bid_should_fail(BUYER_2, InvestorType::Professional, BIDDER_2); +// bid_should_fail(BIDDER_2, InvestorType::Retail, BIDDER_2); +// bid_should_fail(BUYER_2, InvestorType::Retail, BIDDER_2); +// +// // Bidder 3 has a partial winning bid. Same conditions as before should apply. +// bid_should_fail(BIDDER_3, InvestorType::Institutional, BIDDER_3); +// bid_should_fail(BUYER_3, InvestorType::Institutional, BIDDER_3); +// bid_should_fail(BIDDER_3, InvestorType::Professional, BIDDER_3); +// bid_should_fail(BUYER_3, InvestorType::Professional, BIDDER_3); +// bid_should_fail(BIDDER_3, InvestorType::Retail, BIDDER_3); +// bid_should_fail(BUYER_3, InvestorType::Retail, BIDDER_3); +// } +// +// #[test] +// fn per_credential_type_ticket_size_minimums() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.contributing_ticket_sizes = ContributingTicketSizes { +// retail: TicketSize::new(10 * USD_UNIT, None), +// professional: TicketSize::new(100_000 * USD_UNIT, None), +// institutional: TicketSize::new(200_000 * USD_UNIT, None), +// phantom: Default::default(), +// }; +// +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// ); +// +// inst.mint_plmc_to(vec![ +// (BUYER_1, 50_000 * CT_UNIT).into(), +// (BUYER_2, 50_000 * CT_UNIT).into(), +// (BUYER_3, 50_000 * CT_UNIT).into(), +// ]); +// +// inst.mint_foreign_asset_to(vec![ +// (BUYER_1, 50_000 * USD_UNIT).into(), +// (BUYER_2, 50_000 * USD_UNIT).into(), +// (BUYER_3, 50_000 * USD_UNIT).into(), +// ]); +// +// // contribution below 1 CT (10 USD) should fail for retail +// let jwt = get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_1), +// jwt, +// project_id, +// CT_UNIT / 2, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooLow +// ); +// }); +// // contribution below 10_000 CT (100k USD) should fail for professionals +// let jwt = get_mock_jwt_with_cid( +// BUYER_2, +// InvestorType::Professional, +// generate_did_from_account(BUYER_2), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_2), +// jwt, +// project_id, +// 9_999, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooLow +// ); +// }); +// +// // contribution below 20_000 CT (200k USD) should fail for institutionals +// let jwt = get_mock_jwt_with_cid( +// BUYER_3, +// InvestorType::Professional, +// generate_did_from_account(BUYER_3), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_3), +// jwt, +// project_id, +// 19_999, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooLow +// ); +// }); +// } +// +// #[test] +// fn per_credential_type_ticket_size_maximums() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.contributing_ticket_sizes = ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, Some(100_000 * USD_UNIT)), +// professional: TicketSize::new(USD_UNIT, Some(20_000 * USD_UNIT)), +// institutional: TicketSize::new(USD_UNIT, Some(50_000 * USD_UNIT)), +// phantom: Default::default(), +// }; +// +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// ); +// +// inst.mint_plmc_to(vec![ +// (BUYER_1, 500_000 * CT_UNIT).into(), +// (BUYER_2, 500_000 * CT_UNIT).into(), +// (BUYER_3, 500_000 * CT_UNIT).into(), +// (BUYER_4, 500_000 * CT_UNIT).into(), +// (BUYER_5, 500_000 * CT_UNIT).into(), +// (BUYER_6, 500_000 * CT_UNIT).into(), +// ]); +// +// inst.mint_foreign_asset_to(vec![ +// (BUYER_1, 500_000 * USD_UNIT).into(), +// (BUYER_2, 500_000 * USD_UNIT).into(), +// (BUYER_3, 500_000 * USD_UNIT).into(), +// (BUYER_4, 500_000 * USD_UNIT).into(), +// (BUYER_5, 500_000 * USD_UNIT).into(), +// (BUYER_6, 500_000 * USD_UNIT).into(), +// ]); +// +// let buyer_1_jwt = get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let buyer_2_jwt_same_did = get_mock_jwt_with_cid( +// BUYER_2, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// // total contributions with same DID above 10k CT (100k USD) should fail for retail +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_1), +// buyer_1_jwt, +// project_id, +// 9000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_2), +// buyer_2_jwt_same_did.clone(), +// project_id, +// 1001 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooHigh +// ); +// }); +// // bidding 2k total works +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_2), +// buyer_2_jwt_same_did, +// project_id, +// 1000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// +// let buyer_3_jwt = get_mock_jwt_with_cid( +// BUYER_3, +// InvestorType::Professional, +// generate_did_from_account(BUYER_3), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let buyer_4_jwt_same_did = get_mock_jwt_with_cid( +// BUYER_4, +// InvestorType::Professional, +// generate_did_from_account(BUYER_3), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// // total contributions with same DID above 2k CT (20k USD) should fail for professionals +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_3), +// buyer_3_jwt, +// project_id, +// 1800 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_4), +// buyer_4_jwt_same_did.clone(), +// project_id, +// 201 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooHigh +// ); +// }); +// // bidding 2k total works +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_4), +// buyer_4_jwt_same_did, +// project_id, +// 200 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// +// let buyer_5_jwt = get_mock_jwt_with_cid( +// BUYER_5, +// InvestorType::Institutional, +// generate_did_from_account(BUYER_5), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let buyer_6_jwt_same_did = get_mock_jwt_with_cid( +// BUYER_6, +// InvestorType::Institutional, +// generate_did_from_account(BUYER_5), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// // total contributions with same DID above 5k CT (50 USD) should fail for institutionals +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_5), +// buyer_5_jwt, +// project_id, +// 4690 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_6), +// buyer_6_jwt_same_did.clone(), +// project_id, +// 311 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooHigh +// ); +// }); +// // bidding 5k total works +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_6), +// buyer_6_jwt_same_did, +// project_id, +// 310 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// } +// +// #[test] +// fn insufficient_funds() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// ); +// +// let jwt = get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let contribution = ContributionParams::new(BUYER_1, 1_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// // 1 unit less native asset than needed +// let plmc_funding = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_funding.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// inst.execute(|| Balances::burn_from(&BUYER_1, 1, Precision::BestEffort, Fortitude::Force)).unwrap(); +// +// let foreign_funding = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// inst.mint_foreign_asset_to(foreign_funding.clone()); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_1), +// jwt.clone(), +// project_id, +// 1_000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// +// // 1 unit less funding asset than needed +// let plmc_funding = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_funding.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// let foreign_funding = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// +// inst.execute(|| ForeignAssets::set_balance(AcceptedFundingAsset::USDT.to_assethub_id(), &BUYER_1, 0)); +// inst.mint_foreign_asset_to(foreign_funding.clone()); +// +// inst.execute(|| { +// ForeignAssets::burn_from( +// AcceptedFundingAsset::USDT.to_assethub_id(), +// &BUYER_1, +// 100, +// Precision::BestEffort, +// Fortitude::Force, +// ) +// }) +// .unwrap(); +// +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_1), +// jwt, +// project_id, +// 1_000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// } +// +// #[test] +// fn called_outside_community_round() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let created_project = inst.create_new_project(default_project_metadata(ISSUER_1), ISSUER_1, None); +// let evaluating_project = inst.create_evaluating_project(default_project_metadata(ISSUER_2), ISSUER_2, None); +// let auctioning_project = inst.create_auctioning_project( +// default_project_metadata(ISSUER_3), +// ISSUER_3, +// None, +// default_evaluations(), +// ); +// let remaining_project = inst.create_remainder_contributing_project( +// default_project_metadata(ISSUER_4), +// ISSUER_4, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// ); +// let finished_project = inst.create_finished_project( +// default_project_metadata(ISSUER_5), +// ISSUER_5, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// +// let projects = +// vec![created_project, evaluating_project, auctioning_project, remaining_project, finished_project]; +// for project in projects { +// let project_policy = inst.get_project_metadata(project).policy_ipfs_cid.unwrap(); +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_1), +// get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_policy +// ), +// project, +// 1000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT +// ), +// Error::::IncorrectRound +// ); +// }); +// } +// } +// +// #[test] +// fn contribute_with_unaccepted_currencies() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let mut project_metadata_usdt = default_project_metadata(ISSUER_2); +// project_metadata_usdt.participation_currencies = bounded_vec![AcceptedFundingAsset::USDT]; +// +// let mut project_metadata_usdc = default_project_metadata(ISSUER_3); +// project_metadata_usdc.participation_currencies = bounded_vec![AcceptedFundingAsset::USDC]; +// +// let evaluations = default_evaluations(); +// +// let usdt_bids = default_bids() +// .into_iter() +// .map(|mut b| { +// b.asset = AcceptedFundingAsset::USDT; +// b +// }) +// .collect::>(); +// +// let usdc_bids = default_bids() +// .into_iter() +// .map(|mut b| { +// b.asset = AcceptedFundingAsset::USDC; +// b +// }) +// .collect::>(); +// +// let usdt_contribution = ContributionParams::new(BUYER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let usdc_contribution = ContributionParams::new(BUYER_2, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); +// let dot_contribution = ContributionParams::new(BUYER_3, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); +// +// let project_id_usdc = inst.create_community_contributing_project( +// project_metadata_usdc, +// ISSUER_2, +// None, +// evaluations.clone(), +// usdc_bids.clone(), +// ); +// assert_err!( +// inst.contribute_for_users(project_id_usdc, vec![usdt_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// +// let project_id_usdt = inst.create_community_contributing_project( +// project_metadata_usdt, +// ISSUER_3, +// None, +// evaluations.clone(), +// usdt_bids.clone(), +// ); +// assert_err!( +// inst.contribute_for_users(project_id_usdt, vec![usdc_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_err!( +// inst.contribute_for_users(project_id_usdt, vec![dot_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// } +// +// #[test] +// fn cannot_use_evaluation_bond_on_another_project_contribution() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata_1 = default_project_metadata(ISSUER_1); +// let project_metadata_2 = default_project_metadata(ISSUER_2); +// +// let mut evaluations_1 = default_evaluations(); +// let evaluations_2 = default_evaluations(); +// +// let evaluator_contributor = 69; +// let evaluation_amount = 420 * USD_UNIT; +// let evaluator_contribution = +// ContributionParams::new(evaluator_contributor, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// evaluations_1.push((evaluator_contributor, evaluation_amount).into()); +// +// let _project_id_1 = inst.create_community_contributing_project( +// project_metadata_1.clone(), +// ISSUER_1, +// None, +// evaluations_1, +// default_bids(), +// ); +// let project_id_2 = inst.create_community_contributing_project( +// project_metadata_2.clone(), +// ISSUER_2, +// None, +// evaluations_2, +// default_bids(), +// ); +// +// let wap = inst.get_project_details(project_id_2).weighted_average_price.unwrap(); +// +// // Necessary Mints +// let already_bonded_plmc = inst +// .calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount).into()], false)[0] +// .plmc_amount; +// let usable_evaluation_plmc = +// already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; +// let necessary_plmc_for_contribution = +// inst.calculate_contributed_plmc_spent(vec![evaluator_contribution.clone()], wap, false)[0].plmc_amount; +// let necessary_usdt_for_contribution = +// inst.calculate_contributed_funding_asset_spent(vec![evaluator_contribution.clone()], wap); +// inst.mint_plmc_to(vec![UserToPLMCBalance::new( +// evaluator_contributor, +// necessary_plmc_for_contribution - usable_evaluation_plmc, +// )]); +// inst.mint_foreign_asset_to(necessary_usdt_for_contribution); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::contribute( +// RuntimeOrigin::signed(evaluator_contributor), +// get_mock_jwt_with_cid( +// evaluator_contributor, +// InvestorType::Retail, +// generate_did_from_account(evaluator_contributor), +// project_metadata_2.clone().policy_ipfs_cid.unwrap() +// ), +// project_id_2, +// evaluator_contribution.amount, +// evaluator_contribution.multiplier, +// evaluator_contribution.asset +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// } +// +// #[test] +// fn wrong_policy_on_jwt() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// ); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_1), +// get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// "wrong_cid".as_bytes().to_vec().try_into().unwrap() +// ), +// project_id, +// 5000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT +// ), +// Error::::PolicyMismatch +// ); +// }); +// } +// } +// } diff --git a/pallets/funding/src/tests/5_remainder.rs b/pallets/funding/src/tests/5_remainder.rs index 13f13f6e8..bbac497f3 100644 --- a/pallets/funding/src/tests/5_remainder.rs +++ b/pallets/funding/src/tests/5_remainder.rs @@ -1,1888 +1,1888 @@ -use super::*; -// use crate::instantiator::async_features::create_multiple_projects_at; -use frame_support::{dispatch::DispatchResultWithPostInfo, traits::fungibles::metadata::Inspect}; -use sp_runtime::bounded_vec; -use std::collections::HashSet; - -#[cfg(test)] -mod round_flow { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn remainder_round_works() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let _ = inst.create_finished_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - } - - #[test] - fn remainder_round_ends_on_all_ct_sold_exact() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_remainder_contributing_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - ); - const BOB: AccountId = 808; - - let remaining_ct = inst.get_project_details(project_id).remaining_contribution_tokens; - let ct_price = inst.get_project_details(project_id).weighted_average_price.expect("CT Price should exist"); - - let contributions = vec![ContributionParams::new(BOB, remaining_ct, 1u8, AcceptedFundingAsset::USDT)]; - let plmc_fundings = inst.calculate_contributed_plmc_spent(contributions.clone(), ct_price, false); - let plmc_existential_deposits = contributions.accounts().existential_deposits(); - let foreign_asset_fundings = - inst.calculate_contributed_funding_asset_spent(contributions.clone(), ct_price); - - inst.mint_plmc_to(plmc_fundings.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - inst.mint_foreign_asset_to(foreign_asset_fundings.clone()); - - // Buy remaining CTs - inst.contribute_for_users(project_id, contributions) - .expect("The Buyer should be able to buy the exact amount of remaining CTs"); - inst.advance_time(2u64).unwrap(); - - // Check remaining CTs is 0 - assert_eq!( - inst.get_project_details(project_id).remaining_contribution_tokens, - 0, - "There are still remaining CTs" - ); - - // Check project is in FundingEnded state - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - - inst.do_free_plmc_assertions(plmc_existential_deposits); - inst.do_free_foreign_asset_assertions(vec![UserToForeignAssets::::new( - BOB, - 0_u128, - AcceptedFundingAsset::USDT.to_assethub_id(), - )]); - inst.do_reserved_plmc_assertions( - vec![plmc_fundings[0].clone()], - HoldReason::Participation(project_id).into(), - ); - inst.do_contribution_transferred_foreign_asset_assertions(foreign_asset_fundings, project_id); - } - - #[test] - fn round_has_total_ct_allocation_minus_auction_sold() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let evaluations = default_evaluations(); - let bids = default_bids(); - - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - evaluations.clone(), - bids.clone(), - vec![], - ); - let project_details = inst.get_project_details(project_id); - let bid_ct_sold: BalanceOf = inst.execute(|| { - Bids::::iter_prefix_values((project_id,)) - .fold(Zero::zero(), |acc, bid| acc + bid.final_ct_amount) - }); - assert_eq!( - project_details.remaining_contribution_tokens, - project_metadata.total_allocation_size - bid_ct_sold - ); - - let contributions = vec![(BUYER_1, project_details.remaining_contribution_tokens).into()]; - - let plmc_contribution_funding = inst.calculate_contributed_plmc_spent( - contributions.clone(), - project_details.weighted_average_price.unwrap(), - false, - ); - let plmc_existential_deposits = plmc_contribution_funding.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_contribution_funding.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - let foreign_asset_contribution_funding = inst.calculate_contributed_funding_asset_spent( - contributions.clone(), - project_details.weighted_average_price.unwrap(), - ); - inst.mint_foreign_asset_to(foreign_asset_contribution_funding.clone()); - - inst.contribute_for_users(project_id, contributions).unwrap(); - - assert_eq!(inst.get_project_details(project_id).remaining_contribution_tokens, 0); - } - - #[test] - fn different_decimals_ct_works_as_expected() { - // Setup some base values to compare different decimals - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let ed = inst.get_ed(); - let default_project_metadata = default_project_metadata(ISSUER_1); - let original_decimal_aware_price = default_project_metadata.minimum_price; - let original_price = ::PriceProvider::convert_back_to_normal_price( - original_decimal_aware_price, - USD_DECIMALS, - default_project_metadata.token_information.decimals, - ) - .unwrap(); - let usable_plmc_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - PLMC_FOREIGN_ID, - USD_DECIMALS, - PLMC_DECIMALS, - ) - .unwrap() - }); - let usdt_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::USDT.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::USDT.to_assethub_id()), - ) - .unwrap() - }); - let usdc_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::USDC.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::USDC.to_assethub_id()), - ) - .unwrap() - }); - let dot_price = inst.execute(|| { - ::PriceProvider::get_decimals_aware_price( - AcceptedFundingAsset::DOT.to_assethub_id(), - USD_DECIMALS, - ForeignAssets::decimals(AcceptedFundingAsset::DOT.to_assethub_id()), - ) - .unwrap() - }); - - let mut funding_assets_cycle = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] - .into_iter() - .cycle(); - - let mut total_fundings_ct = Vec::new(); - let mut total_fundings_usd = Vec::new(); - let mut total_fundings_plmc = Vec::new(); - - let mut decimal_test = |decimals: u8| { - let funding_asset = funding_assets_cycle.next().unwrap(); - let funding_asset_usd_price = match funding_asset { - AcceptedFundingAsset::USDT => usdt_price, - AcceptedFundingAsset::USDC => usdc_price, - AcceptedFundingAsset::DOT => dot_price, - }; - - let mut project_metadata = default_project_metadata.clone(); - project_metadata.token_information.decimals = decimals; - project_metadata.minimum_price = - ::PriceProvider::calculate_decimals_aware_price( - original_price, - USD_DECIMALS, - decimals, - ) - .unwrap(); - - project_metadata.total_allocation_size = 1_000_000 * 10u128.pow(decimals as u32); - project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; - project_metadata.participation_currencies = bounded_vec!(funding_asset); - - let issuer: AccountIdOf = (10_000 + inst.get_new_nonce()).try_into().unwrap(); - let evaluations = inst.generate_successful_evaluations( - project_metadata.clone(), - default_evaluators(), - default_weights(), - ); - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - issuer, - None, - evaluations, - vec![], - vec![], - ); - - let total_funding_ct = project_metadata.total_allocation_size; - let total_funding_usd = project_metadata.minimum_price.saturating_mul_int(total_funding_ct); - let total_funding_plmc = usable_plmc_price.reciprocal().unwrap().saturating_mul_int(total_funding_usd); - let total_funding_funding_asset = - funding_asset_usd_price.reciprocal().unwrap().saturating_mul_int(total_funding_usd); - - total_fundings_ct.push(total_funding_ct); - total_fundings_usd.push(total_funding_usd); - total_fundings_plmc.push(total_funding_plmc); - - // Every project should want to raise 10MM USD - assert_eq!(total_funding_usd, 10_000_000 * USD_UNIT); - - // Every project should produce the same PLMC bond when having the full funding at multiplier 1. - assert_close_enough!(total_funding_plmc, 1_190_476 * PLMC, Perquintill::from_float(0.999)); - - // Every project should have a different amount of CTs to raise, depending on their decimals - assert_eq!(total_funding_ct, 1_000_000 * 10u128.pow(decimals as u32)); - - // Buying all the remaining tokens. This is a fixed USD value, but the extrinsic amount depends on CT decimals. - inst.mint_plmc_to(vec![UserToPLMCBalance::new(BUYER_1, total_funding_plmc + ed)]); - inst.mint_foreign_asset_to(vec![UserToForeignAssets::new( - BUYER_1, - total_funding_funding_asset, - funding_asset.to_assethub_id(), - )]); - - assert_ok!(inst.execute(|| PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_1), - get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - total_funding_ct, - 1u8.try_into().unwrap(), - funding_asset, - ))); - - // the remaining tokens should be zero - assert_eq!(inst.get_project_details(project_id).remaining_contribution_tokens, 0); - - // We can successfully finish the project - inst.finish_funding(project_id, None).unwrap(); - }; - - for decimals in 6..=18 { - decimal_test(decimals); - } - - // Since we use the same original price and allocation size and adjust for decimals, - // the USD and PLMC amounts should be the same - assert!(total_fundings_usd.iter().all(|x| *x == total_fundings_usd[0])); - assert!(total_fundings_plmc.iter().all(|x| *x == total_fundings_plmc[0])); - - // CT amounts however should be different from each other - let mut hash_set_1 = HashSet::new(); - for amount in total_fundings_ct { - assert!(!hash_set_1.contains(&amount)); - hash_set_1.insert(amount); - } - } - } -} - -#[cfg(test)] -mod contribute_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - use frame_support::traits::fungible::InspectFreeze; - use pallet_balances::AccountData; - - #[test] - fn evaluation_bond_counts_towards_contribution() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - - const BOB: AccountId = 42069; - const CARL: AccountId = 420691; - let mut evaluations = default_evaluations(); - let bob_evaluation: UserToUSDBalance = (BOB, 1337 * USD_UNIT).into(); - let carl_evaluation: UserToUSDBalance = (CARL, 1337 * USD_UNIT).into(); - evaluations.push(bob_evaluation.clone()); - evaluations.push(carl_evaluation.clone()); - - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - evaluations, - default_bids(), - vec![], - ); - let ct_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let plmc_price = ::PriceProvider::get_decimals_aware_price( - PLMC_FOREIGN_ID, - USD_DECIMALS, - PLMC_DECIMALS, - ) - .unwrap(); - - let evaluation_plmc_bond = - inst.execute(|| Balances::balance_on_hold(&HoldReason::Evaluation(project_id).into(), &BOB)); - let slashable_plmc = ::EvaluatorSlash::get() * evaluation_plmc_bond; - let usable_plmc = evaluation_plmc_bond - slashable_plmc; - - let usable_usd = plmc_price.checked_mul_int(usable_plmc).unwrap(); - let slashable_usd = plmc_price.checked_mul_int(slashable_plmc).unwrap(); - - let usable_ct = ct_price.reciprocal().unwrap().saturating_mul_int(usable_usd); - let slashable_ct = ct_price.reciprocal().unwrap().saturating_mul_int(slashable_usd); - - // Can't contribute with only the evaluation bond - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BOB), - get_mock_jwt_with_cid( - BOB, - InvestorType::Retail, - generate_did_from_account(BOB), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - usable_ct + slashable_ct, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::ParticipantNotEnoughFunds - ); - }); - - // Can partially use the usable evaluation bond (half in this case) - let contribution_usdt = - inst.calculate_contributed_funding_asset_spent(vec![(BOB, usable_ct / 2).into()], ct_price); - inst.mint_foreign_asset_to(contribution_usdt.clone()); - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(BOB), - get_mock_jwt_with_cid( - BOB, - InvestorType::Retail, - generate_did_from_account(BOB), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - usable_ct / 2, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - - // Can use the full evaluation bond - let contribution_usdt = - inst.calculate_contributed_funding_asset_spent(vec![(CARL, usable_ct).into()], ct_price); - inst.mint_foreign_asset_to(contribution_usdt.clone()); - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(CARL), - get_mock_jwt_with_cid( - CARL, - InvestorType::Retail, - generate_did_from_account(CARL), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - usable_ct, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - } - - #[test] - fn evaluation_bond_used_on_failed_bid_can_be_reused_on_contribution() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let bob = 42069; - let project_metadata = default_project_metadata(ISSUER_1); - // An evaluator that did a bid but it was not accepted at the end of the auction, can use that PLMC for contributing - let mut evaluations = default_evaluations(); - let bob_evaluation = (bob, 1337 * USD_UNIT).into(); - evaluations.push(bob_evaluation); - - let bids = default_bids(); - let bob_bid: BidParams = (bob, 1337 * CT_UNIT).into(); - let all_bids = bids.iter().chain(vec![bob_bid.clone()].iter()).cloned().collect_vec(); - - let project_id = - inst.create_auctioning_project(default_project_metadata(ISSUER_2), ISSUER_2, None, evaluations); - - let evaluation_plmc_bond = - inst.execute(|| Balances::balance_on_hold(&HoldReason::Evaluation(project_id).into(), &bob)); - let slashable_plmc_bond = ::EvaluatorSlash::get() * evaluation_plmc_bond; - let usable_plmc_bond = evaluation_plmc_bond - slashable_plmc_bond; - - let bids_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &all_bids, - project_metadata.clone(), - None, - true, - ); - inst.mint_plmc_to(bids_plmc.clone()); - - let bids_foreign = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &all_bids, - project_metadata.clone(), - None, - ); - inst.mint_foreign_asset_to(bids_foreign.clone()); - - inst.bid_for_users(project_id, bids).unwrap(); - - let auction_end = ::AuctionOpeningDuration::get() + - ::AuctionClosingDuration::get(); - inst.advance_time(auction_end - 1).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Auction); - inst.bid_for_users(project_id, vec![bob_bid]).unwrap(); - - inst.start_community_funding(project_id).unwrap(); - assert!(matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(..))); - inst.start_remainder_or_end_funding(project_id).unwrap(); - - let plmc_price = ::PriceProvider::get_decimals_aware_price( - PLMC_FOREIGN_ID, - USD_DECIMALS, - PLMC_DECIMALS, - ) - .unwrap(); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let usable_usd = plmc_price.saturating_mul_int(usable_plmc_bond); - let usable_ct = wap.reciprocal().unwrap().saturating_mul_int(usable_usd); - - let bob_contribution = (bob, 1337 * CT_UNIT).into(); - let contribution_usdt = inst.calculate_contributed_funding_asset_spent(vec![bob_contribution], wap); - inst.mint_foreign_asset_to(contribution_usdt.clone()); - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(bob), - get_mock_jwt_with_cid( - bob, - InvestorType::Retail, - generate_did_from_account(bob), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - usable_ct, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - } - - #[test] - fn contribute_with_multiple_currencies() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let mut project_metadata_all = default_project_metadata(ISSUER_1); - project_metadata_all.participation_currencies = - vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] - .try_into() - .unwrap(); - - let mut project_metadata_usdt = default_project_metadata(ISSUER_2); - project_metadata_usdt.participation_currencies = vec![AcceptedFundingAsset::USDT].try_into().unwrap(); - - let mut project_metadata_usdc = default_project_metadata(ISSUER_3); - project_metadata_usdc.participation_currencies = vec![AcceptedFundingAsset::USDC].try_into().unwrap(); - - let mut project_metadata_dot = default_project_metadata(ISSUER_4); - project_metadata_dot.participation_currencies = vec![AcceptedFundingAsset::DOT].try_into().unwrap(); - - let evaluations = default_evaluations(); - - let usdt_bids = default_bids() - .into_iter() - .map(|mut b| { - b.asset = AcceptedFundingAsset::USDT; - b - }) - .collect::>(); - - let usdc_bids = default_bids() - .into_iter() - .map(|mut b| { - b.asset = AcceptedFundingAsset::USDC; - b - }) - .collect::>(); - - let dot_bids = default_bids() - .into_iter() - .map(|mut b| { - b.asset = AcceptedFundingAsset::DOT; - b - }) - .collect::>(); - - let usdt_contribution = ContributionParams::new(BUYER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let usdc_contribution = ContributionParams::new(BUYER_2, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); - let dot_contribution = ContributionParams::new(BUYER_3, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); - - let project_id_all = inst.create_remainder_contributing_project( - project_metadata_all.clone(), - ISSUER_1, - None, - evaluations.clone(), - default_bids(), - vec![], - ); - let wap = inst.get_project_details(project_id_all).weighted_average_price.unwrap(); - - let plmc_fundings = inst.calculate_contributed_plmc_spent( - vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()], - wap, - false, - ); - let plmc_existential_deposits = plmc_fundings.accounts().existential_deposits(); - let plmc_all_mints = - inst.generic_map_operation(vec![plmc_fundings, plmc_existential_deposits], MergeOperation::Add); - inst.mint_plmc_to(plmc_all_mints.clone()); - inst.mint_plmc_to(plmc_all_mints.clone()); - inst.mint_plmc_to(plmc_all_mints.clone()); - - let usdt_fundings = inst.calculate_contributed_funding_asset_spent( - vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()], - wap, - ); - inst.mint_foreign_asset_to(usdt_fundings.clone()); - inst.mint_foreign_asset_to(usdt_fundings.clone()); - inst.mint_foreign_asset_to(usdt_fundings.clone()); - - assert_ok!(inst.contribute_for_users( - project_id_all, - vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()] - )); - - let project_id_usdt = inst.create_remainder_contributing_project( - project_metadata_usdt.clone(), - ISSUER_2, - None, - evaluations.clone(), - usdt_bids, - vec![], - ); - - assert_ok!(inst.contribute_for_users(project_id_usdt, vec![usdt_contribution.clone()])); - assert_err!( - inst.contribute_for_users(project_id_usdt, vec![usdc_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_err!( - inst.contribute_for_users(project_id_usdt, vec![dot_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - - let project_id_usdc = inst.create_remainder_contributing_project( - project_metadata_usdc.clone(), - ISSUER_3, - None, - evaluations.clone(), - usdc_bids, - vec![], - ); - - assert_err!( - inst.contribute_for_users(project_id_usdc, vec![usdt_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_ok!(inst.contribute_for_users(project_id_usdc, vec![usdc_contribution.clone()])); - assert_err!( - inst.contribute_for_users(project_id_usdc, vec![dot_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - - let project_id_dot = inst.create_remainder_contributing_project( - project_metadata_dot.clone(), - ISSUER_4, - None, - evaluations.clone(), - dot_bids, - vec![], - ); - - assert_err!( - inst.contribute_for_users(project_id_dot, vec![usdt_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_err!( - inst.contribute_for_users(project_id_dot, vec![usdc_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_ok!(inst.contribute_for_users(project_id_dot, vec![dot_contribution.clone()])); - } - - fn test_contribution_setup( - inst: &mut MockInstantiator, - project_id: ProjectId, - contributor: AccountIdOf, - investor_type: InvestorType, - u8_multiplier: u8, - ) -> DispatchResultWithPostInfo { - let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); - let jwt = get_mock_jwt_with_cid( - contributor.clone(), - investor_type, - generate_did_from_account(contributor), - project_policy, - ); - let amount = 1000 * CT_UNIT; - let multiplier = Multiplier::force_new(u8_multiplier); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - if u8_multiplier > 0 { - let contribution = ContributionParams:: { - contributor: contributor.clone(), - amount, - multiplier, - asset: AcceptedFundingAsset::USDT, - }; - - let necessary_plmc = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let plmc_existential_amounts = necessary_plmc.accounts().existential_deposits(); - let necessary_usdt = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - - inst.mint_plmc_to(necessary_plmc.clone()); - inst.mint_plmc_to(plmc_existential_amounts.clone()); - inst.mint_foreign_asset_to(necessary_usdt.clone()); - } - inst.execute(|| { - Pallet::::contribute( - RuntimeOrigin::signed(contributor), - jwt, - project_id, - amount, - multiplier, - AcceptedFundingAsset::USDT, - ) - }) - } - - #[test] - fn non_retail_multiplier_limits() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut project_metadata = default_project_metadata(ISSUER_1); - project_metadata.mainnet_token_max_supply = 80_000_000 * CT_UNIT; - project_metadata.total_allocation_size = 10_000_000 * CT_UNIT; - project_metadata.bidding_ticket_sizes = BiddingTicketSizes { - professional: TicketSize::new(5000 * USD_UNIT, None), - institutional: TicketSize::new(5000 * USD_UNIT, None), - phantom: Default::default(), - }; - project_metadata.contributing_ticket_sizes = ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, None), - professional: TicketSize::new(USD_UNIT, None), - institutional: TicketSize::new(USD_UNIT, None), - phantom: Default::default(), - }; - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); - let bids = inst.generate_bids_from_total_ct_percent( - project_metadata.clone(), - 50, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - evaluations, - bids, - vec![], - ); - - // Professional contributions: 0x multiplier should fail - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Professional, 0), - Error::::ForbiddenMultiplier - ); - // Professional contributions: 1 - 10x multiplier should work - for multiplier in 1..=10u8 { - assert_ok!(test_contribution_setup( - &mut inst, - project_id, - BUYER_1, - InvestorType::Professional, - multiplier - )); - } - // Professional contributions: >=11x multiplier should fail - for multiplier in 11..=50u8 { - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Professional, multiplier), - Error::::ForbiddenMultiplier - ); - } - - // Institutional contributions: 0x multiplier should fail - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_2, InvestorType::Institutional, 0), - Error::::ForbiddenMultiplier - ); - // Institutional contributions: 1 - 25x multiplier should work - for multiplier in 1..=25u8 { - assert_ok!(test_contribution_setup( - &mut inst, - project_id, - BUYER_2, - InvestorType::Institutional, - multiplier - )); - } - // Institutional contributions: >=26x multiplier should fail - for multiplier in 26..=50u8 { - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_2, InvestorType::Institutional, multiplier), - Error::::ForbiddenMultiplier - ); - } - } - - #[test] - fn retail_multiplier_limits() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let mut issuer: AccountId = 6969420; - - let mut create_project = |inst: &mut MockInstantiator| { - issuer += 1; - inst.create_remainder_contributing_project( - default_project_metadata(issuer), - issuer, - None, - default_evaluations(), - default_bids(), - vec![], - ) - }; - - let max_allowed_multipliers_map = vec![(2, 1), (4, 2), (9, 4), (24, 7), (25, 10)]; - - let mut previous_projects_created = 0; - for (projects_participated_amount, max_allowed_multiplier) in max_allowed_multipliers_map { - (previous_projects_created..projects_participated_amount - 1).for_each(|_| { - let project_id = create_project(&mut inst); - assert_ok!(test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, 1)); - }); - - let project_id = create_project(&mut inst); - previous_projects_created = projects_participated_amount; - - // 0x multiplier should fail - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, 0), - Error::::ForbiddenMultiplier - ); - - // Multipliers that should work - for multiplier in 1..=max_allowed_multiplier { - assert_ok!(test_contribution_setup( - &mut inst, - project_id, - BUYER_1, - InvestorType::Retail, - multiplier - )); - } - - // Multipliers that should NOT work - for multiplier in max_allowed_multiplier + 1..=50 { - assert_err!( - test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, multiplier), - Error::::ForbiddenMultiplier - ); - } - } - } - - #[test] - fn did_with_winning_bid_can_contribute() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let mut evaluations = default_evaluations(); - evaluations.push((BIDDER_4, 1337 * USD_UNIT).into()); - - let successful_bids = vec![ - BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_2, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ]; - let failing_bids_after_random_end = - vec![(BIDDER_3, 25_000 * CT_UNIT).into(), (BIDDER_4, 25_000 * CT_UNIT).into()]; - // This bids should fill the first bucket. - let failing_bids_sold_out = - vec![(BIDDER_5, 250_000 * CT_UNIT).into(), (BIDDER_6, 250_000 * CT_UNIT).into()]; - - let all_bids = failing_bids_sold_out - .iter() - .chain(successful_bids.iter()) - .chain(failing_bids_after_random_end.iter()) - .cloned() - .collect_vec(); - - let project_id = - inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); - - let plmc_fundings = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &all_bids.clone(), - project_metadata.clone(), - None, - true, - ); - inst.mint_plmc_to(plmc_fundings.clone()); - - let foreign_funding = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &all_bids.clone(), - project_metadata.clone(), - None, - ); - inst.mint_foreign_asset_to(foreign_funding.clone()); - - inst.bid_for_users(project_id, failing_bids_sold_out).unwrap(); - inst.bid_for_users(project_id, successful_bids).unwrap(); - inst.advance_time( - ::AuctionOpeningDuration::get() + - ::AuctionClosingDuration::get(), - ) - .unwrap(); - inst.bid_for_users(project_id, failing_bids_after_random_end).unwrap(); - inst.advance_time(2).unwrap(); - assert!(matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(..))); - inst.start_remainder_or_end_funding(project_id).unwrap(); - - // Some low amount of plmc and usdt to cover a purchase of 10CTs. - let plmc_mints = vec![ - (BIDDER_3, 42069 * PLMC).into(), - (BIDDER_4, 42069 * PLMC).into(), - (BIDDER_5, 42069 * PLMC).into(), - (BIDDER_6, 42069 * PLMC).into(), - (BUYER_3, 42069 * PLMC).into(), - (BUYER_4, 42069 * PLMC).into(), - (BUYER_5, 42069 * PLMC).into(), - (BUYER_6, 42069 * PLMC).into(), - ]; - inst.mint_plmc_to(plmc_mints); - let usdt_mints = vec![ - (BIDDER_3, 42069 * CT_UNIT).into(), - (BIDDER_4, 42069 * CT_UNIT).into(), - (BIDDER_5, 42069 * CT_UNIT).into(), - (BIDDER_6, 42069 * CT_UNIT).into(), - (BUYER_3, 42069 * CT_UNIT).into(), - (BUYER_4, 42069 * CT_UNIT).into(), - (BUYER_5, 42069 * CT_UNIT).into(), - (BUYER_6, 42069 * CT_UNIT).into(), - ]; - inst.mint_foreign_asset_to(usdt_mints); - - let mut bid_should_succeed = |account, investor_type, did_acc| { - inst.execute(|| { - assert_ok!(Pallet::::contribute( - RuntimeOrigin::signed(account), - get_mock_jwt_with_cid( - account, - investor_type, - generate_did_from_account(did_acc), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - 10 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - )); - }); - }; - - // Bidder 3 has a losing bid due to bidding after the random end. His did should be able to contribute regardless of what investor type - // or account he uses to sign the transaction - bid_should_succeed(BIDDER_3, InvestorType::Institutional, BIDDER_3); - bid_should_succeed(BUYER_3, InvestorType::Institutional, BIDDER_3); - bid_should_succeed(BIDDER_3, InvestorType::Professional, BIDDER_3); - bid_should_succeed(BUYER_3, InvestorType::Professional, BIDDER_3); - bid_should_succeed(BIDDER_3, InvestorType::Retail, BIDDER_3); - bid_should_succeed(BUYER_3, InvestorType::Retail, BIDDER_3); - - // Bidder 4 has a losing bid due to bidding after the random end, and he was also an evaluator. Same conditions as before should apply. - bid_should_succeed(BIDDER_4, InvestorType::Institutional, BIDDER_4); - bid_should_succeed(BUYER_4, InvestorType::Institutional, BIDDER_4); - bid_should_succeed(BIDDER_4, InvestorType::Professional, BIDDER_4); - bid_should_succeed(BUYER_4, InvestorType::Professional, BIDDER_4); - bid_should_succeed(BIDDER_4, InvestorType::Retail, BIDDER_4); - bid_should_succeed(BUYER_4, InvestorType::Retail, BIDDER_4); - - // Bidder 5 has a losing bid due to CTs being sold out at his price point. Same conditions as before should apply. - bid_should_succeed(BIDDER_5, InvestorType::Institutional, BIDDER_5); - bid_should_succeed(BUYER_5, InvestorType::Institutional, BIDDER_5); - bid_should_succeed(BIDDER_5, InvestorType::Professional, BIDDER_5); - bid_should_succeed(BUYER_5, InvestorType::Professional, BIDDER_5); - bid_should_succeed(BIDDER_5, InvestorType::Retail, BIDDER_5); - bid_should_succeed(BUYER_5, InvestorType::Retail, BIDDER_5); - - // Bidder 6 has a losing bid due to CTs being sold out at his price point, and he was also an evaluator. Same conditions as before should apply. - bid_should_succeed(BIDDER_6, InvestorType::Institutional, BIDDER_6); - bid_should_succeed(BUYER_6, InvestorType::Institutional, BIDDER_6); - bid_should_succeed(BIDDER_6, InvestorType::Professional, BIDDER_6); - bid_should_succeed(BUYER_6, InvestorType::Professional, BIDDER_6); - bid_should_succeed(BIDDER_6, InvestorType::Retail, BIDDER_6); - bid_should_succeed(BUYER_6, InvestorType::Retail, BIDDER_6); - } - - #[test] - fn can_contribute_with_frozen_tokens_funding_failed() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - issuer, - None, - default_evaluations(), - vec![], - vec![], - ); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let contribution = ContributionParams::new(BUYER_4, 500 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let frozen_amount = plmc_required[0].plmc_amount; - let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); - - inst.mint_plmc_to(plmc_existential_deposits); - inst.mint_plmc_to(plmc_required.clone()); - - inst.execute(|| { - mock::Balances::set_freeze(&(), &BUYER_4, plmc_required[0].plmc_amount).unwrap(); - }); - - let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - inst.mint_foreign_asset_to(usdt_required); - - inst.execute(|| { - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(BUYER_4), ISSUER_1, frozen_amount,), - TokenError::Frozen - ); - }); - - inst.execute(|| { - assert_ok!(PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_4), - get_mock_jwt_with_cid( - BUYER_4, - InvestorType::Institutional, - generate_did_from_account(BUYER_4), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - contribution.amount, - contribution.multiplier, - contribution.asset - )); - }); - - inst.finish_funding(project_id, None).unwrap(); - - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - let account_data = inst.execute(|| System::account(&BUYER_4)).data; - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - let expected_account_data = AccountData { - free: inst.get_ed(), - reserved: frozen_amount, - frozen: frozen_amount, - flags: Default::default(), - }; - assert_eq!(account_data, expected_account_data); - - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - inst.execute(|| { - PolimecFunding::settle_failed_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0) - .unwrap(); - }); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Evaluation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - let account_data = inst.execute(|| System::account(&BUYER_4)).data; - - assert_eq!(free_balance, inst.get_ed() + frozen_amount); - assert_eq!(bid_held_balance, Zero::zero()); - assert_eq!(frozen_balance, frozen_amount); - let expected_account_data = AccountData { - free: inst.get_ed() + frozen_amount, - reserved: Zero::zero(), - frozen: frozen_amount, - flags: Default::default(), - }; - assert_eq!(account_data, expected_account_data); - assert_eq!(account_data.frozen, account_data.free - inst.get_ed()); - } - - #[test] - fn can_contribute_with_frozen_tokens_funding_success() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - issuer, - None, - default_evaluations(), - default_bids(), - vec![], - ); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let contribution = ContributionParams::new(BUYER_4, 500 * CT_UNIT, 5u8, AcceptedFundingAsset::USDT); - let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let frozen_amount = plmc_required[0].plmc_amount; - let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); - - inst.mint_plmc_to(plmc_existential_deposits); - inst.mint_plmc_to(plmc_required.clone()); - - inst.execute(|| { - mock::Balances::set_freeze(&(), &BUYER_4, plmc_required[0].plmc_amount).unwrap(); - }); - - let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - inst.mint_foreign_asset_to(usdt_required); - - inst.execute(|| { - assert_noop!( - Balances::transfer_allow_death(RuntimeOrigin::signed(BUYER_4), ISSUER_1, frozen_amount,), - TokenError::Frozen - ); - }); - - inst.execute(|| { - assert_ok!(PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_4), - get_mock_jwt_with_cid( - BUYER_4, - InvestorType::Institutional, - generate_did_from_account(BUYER_4), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - contribution.amount, - contribution.multiplier, - contribution.asset - )); - }); - - inst.finish_funding(project_id, None).unwrap(); - - let decision_block = inst - .get_update_block(project_id, &UpdateType::ProjectDecision(FundingOutcomeDecision::AcceptFunding)) - .unwrap(); - inst.jump_to_block(decision_block); - - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - let account_data = inst.execute(|| System::account(&BUYER_4)).data; - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - let expected_account_data = AccountData { - free: inst.get_ed(), - reserved: frozen_amount, - frozen: frozen_amount, - flags: Default::default(), - }; - assert_eq!(account_data, expected_account_data); - - inst.execute(|| { - PolimecFunding::settle_successful_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0) - .unwrap(); - }); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - let account_data = inst.execute(|| System::account(&BUYER_4)).data; - - assert_eq!(free_balance, inst.get_ed()); - assert_eq!(bid_held_balance, frozen_amount); - assert_eq!(frozen_balance, frozen_amount); - let expected_account_data = AccountData { - free: inst.get_ed(), - reserved: frozen_amount, - frozen: frozen_amount, - flags: Default::default(), - }; - assert_eq!(account_data, expected_account_data); - - let vest_duration = - MultiplierOf::::new(5u8).unwrap().calculate_vesting_duration::(); - let now = inst.current_block(); - inst.jump_to_block(now + vest_duration + 1u64); - inst.execute(|| { - assert_ok!(mock::LinearRelease::vest( - RuntimeOrigin::signed(BUYER_4), - HoldReason::Participation(project_id).into() - )); - }); - - let free_balance = inst.get_free_plmc_balance_for(BUYER_4); - let bid_held_balance = - inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); - let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); - let account_data = inst.execute(|| System::account(&BUYER_4)).data; - - assert_eq!(free_balance, inst.get_ed() + frozen_amount); - assert_eq!(bid_held_balance, Zero::zero()); - assert_eq!(frozen_balance, frozen_amount); - let expected_account_data = AccountData { - free: inst.get_ed() + frozen_amount, - reserved: Zero::zero(), - frozen: frozen_amount, - flags: Default::default(), - }; - assert_eq!(account_data, expected_account_data); - } - - #[test] - fn participant_was_evaluator_and_bidder() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let issuer = ISSUER_1; - let participant = 42069u32; - let project_metadata = default_project_metadata(issuer); - let mut evaluations = default_evaluations(); - evaluations.push((participant, 100 * USD_UNIT).into()); - let mut bids = default_bids(); - bids.push(BidParams::new(participant, 1000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT)); - let community_contributions = default_community_buys(); - let mut remainder_contributions = default_remainder_buys(); - remainder_contributions.push(ContributionParams::new( - participant, - 10 * CT_UNIT, - 1u8, - AcceptedFundingAsset::USDT, - )); - - let _project_id = inst.create_finished_project( - project_metadata.clone(), - issuer, - None, - evaluations, - bids, - community_contributions, - remainder_contributions, - ); - } - } - - #[cfg(test)] - mod failure { - use super::*; - use frame_support::traits::{ - fungible::Mutate, - fungibles::Mutate as OtherMutate, - tokens::{Fortitude, Precision}, - }; - - #[test] - fn contribution_errors_if_user_limit_is_reached() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_remainder_contributing_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - vec![], - ); - const CONTRIBUTOR: AccountIdOf = 420; - - let project_details = inst.get_project_details(project_id); - let token_price = project_details.weighted_average_price.unwrap(); - - // Create a contribution vector that will reach the limit of contributions for a user-project - let token_amount: BalanceOf = CT_UNIT; - let range = 0..::MaxContributionsPerUser::get(); - let contributions: Vec> = range - .map(|_| ContributionParams::new(CONTRIBUTOR, token_amount, 1u8, AcceptedFundingAsset::USDT)) - .collect(); - - let plmc_funding = inst.calculate_contributed_plmc_spent(contributions.clone(), token_price, false); - let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); - - let foreign_funding = inst.calculate_contributed_funding_asset_spent(contributions.clone(), token_price); - - inst.mint_plmc_to(plmc_funding.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - - inst.mint_foreign_asset_to(foreign_funding.clone()); - - // Reach up to the limit of contributions for a user-project - assert!(inst.contribute_for_users(project_id, contributions).is_ok()); - - // Try to contribute again, but it should fail because the limit of contributions for a user-project was reached. - let over_limit_contribution = - ContributionParams::new(CONTRIBUTOR, token_amount, 1u8, AcceptedFundingAsset::USDT); - assert!(inst.contribute_for_users(project_id, vec![over_limit_contribution]).is_err()); - - // Check that the right amount of PLMC is bonded, and funding currency is transferred - let contributor_post_buy_plmc_balance = - inst.execute(|| ::NativeCurrency::balance(&CONTRIBUTOR)); - let contributor_post_buy_foreign_asset_balance = inst.execute(|| { - ::FundingCurrency::balance( - AcceptedFundingAsset::USDT.to_assethub_id(), - CONTRIBUTOR, - ) - }); - - assert_eq!(contributor_post_buy_plmc_balance, inst.get_ed()); - assert_eq!(contributor_post_buy_foreign_asset_balance, 0); - - let plmc_bond_stored = inst.execute(|| { - ::NativeCurrency::balance_on_hold( - &HoldReason::Participation(project_id.into()).into(), - &CONTRIBUTOR, - ) - }); - let foreign_asset_contributions_stored = inst.execute(|| { - Contributions::::iter_prefix_values((project_id, CONTRIBUTOR)) - .map(|c| c.funding_asset_amount) - .sum::>() - }); - - assert_eq!(plmc_bond_stored, inst.sum_balance_mappings(vec![plmc_funding.clone()])); - assert_eq!(foreign_asset_contributions_stored, inst.sum_foreign_mappings(vec![foreign_funding.clone()])); - } - - #[test] - fn issuer_cannot_contribute_his_project() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - ); - assert_err!( - inst.execute(|| crate::Pallet::::do_contribute( - &(&ISSUER_1 + 1), - project_id, - 500 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(ISSUER_1), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )), - Error::::ParticipationToOwnProject - ); - } - - #[test] - fn per_credential_type_ticket_size_minimums() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = ProjectMetadata { - token_information: default_token_information(), - mainnet_token_max_supply: 8_000_000 * CT_UNIT, - total_allocation_size: 1_000_000 * CT_UNIT, - auction_round_allocation_percentage: Percent::from_percent(50u8), - minimum_price: PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(10.0), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(), - bidding_ticket_sizes: BiddingTicketSizes { - professional: TicketSize::new(8000 * USD_UNIT, None), - institutional: TicketSize::new(20_000 * USD_UNIT, None), - phantom: Default::default(), - }, - contributing_ticket_sizes: ContributingTicketSizes { - retail: TicketSize::new(10 * USD_UNIT, None), - professional: TicketSize::new(100_000 * USD_UNIT, None), - institutional: TicketSize::new(200_000 * USD_UNIT, None), - phantom: Default::default(), - }, - participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), - funding_destination_account: ISSUER_1, - policy_ipfs_cid: Some(ipfs_hash()), - }; - - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - vec![], - ); - - inst.mint_plmc_to(vec![ - (BUYER_4, 50_000 * PLMC).into(), - (BUYER_5, 50_000 * PLMC).into(), - (BUYER_6, 50_000 * PLMC).into(), - ]); - - inst.mint_foreign_asset_to(vec![ - (BUYER_4, 50_000 * USDT_UNIT).into(), - (BUYER_5, 50_000 * USDT_UNIT).into(), - (BUYER_6, 50_000 * USDT_UNIT).into(), - ]); - - // contribution below 1 CT (10 USD) should fail for retail - let jwt = get_mock_jwt_with_cid( - BUYER_4, - InvestorType::Retail, - generate_did_from_account(BUYER_4), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_4), - jwt, - project_id, - CT_UNIT / 2, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooLow - ); - }); - // contribution below 10_000 CT (100k USD) should fail for professionals - let jwt = get_mock_jwt_with_cid( - BUYER_5, - InvestorType::Professional, - generate_did_from_account(BUYER_5), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_5), - jwt, - project_id, - 9_999, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooLow - ); - }); - - // contribution below 20_000 CT (200k USD) should fail for institutionals - let jwt = get_mock_jwt_with_cid( - BUYER_6, - InvestorType::Institutional, - generate_did_from_account(BUYER_6), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_6), - jwt, - project_id, - 19_999, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::TooLow - ); - }); - } - - #[test] - fn per_credential_type_ticket_size_maximums() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = ProjectMetadata { - token_information: default_token_information(), - mainnet_token_max_supply: 8_000_000 * CT_UNIT, - total_allocation_size: 1_000_000 * CT_UNIT, - auction_round_allocation_percentage: Percent::from_percent(50u8), - minimum_price: PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(10.0), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(), - bidding_ticket_sizes: BiddingTicketSizes { - professional: TicketSize::new(5000 * USD_UNIT, None), - institutional: TicketSize::new(5000 * USD_UNIT, None), - phantom: Default::default(), - }, - contributing_ticket_sizes: ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, Some(300_000 * USD_UNIT)), - professional: TicketSize::new(USD_UNIT, Some(20_000 * USD_UNIT)), - institutional: TicketSize::new(USD_UNIT, Some(50_000 * USD_UNIT)), - phantom: Default::default(), - }, - participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), - funding_destination_account: ISSUER_1, - policy_ipfs_cid: Some(ipfs_hash()), - }; - - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - vec![], - ); - - inst.mint_plmc_to(vec![ - (BUYER_4, 500_000 * PLMC).into(), - (BUYER_5, 500_000 * PLMC).into(), - (BUYER_6, 500_000 * PLMC).into(), - (BUYER_7, 500_000 * PLMC).into(), - (BUYER_8, 500_000 * PLMC).into(), - (BUYER_9, 500_000 * PLMC).into(), - ]); - - inst.mint_foreign_asset_to(vec![ - (BUYER_4, 500_000 * USDT_UNIT).into(), - (BUYER_5, 500_000 * USDT_UNIT).into(), - (BUYER_6, 500_000 * USDT_UNIT).into(), - (BUYER_7, 500_000 * USDT_UNIT).into(), - (BUYER_8, 500_000 * USDT_UNIT).into(), - (BUYER_9, 500_000 * USDT_UNIT).into(), - ]); - - // total contributions with same DID above 30k CT (300k USD) should fail for retail - inst.execute(|| { - assert_ok!(Pallet::::do_contribute( - &BUYER_4, - project_id, - 28_000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(BUYER_4), - InvestorType::Retail, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::do_contribute( - &BUYER_5, - project_id, - 2001 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - // note we use the same did as bidder 1, on a different account - generate_did_from_account(BUYER_4), - InvestorType::Retail, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - Error::::TooHigh - ); - }); - // bidding 2k total works - inst.execute(|| { - assert_ok!(Pallet::::do_contribute( - &BUYER_5, - project_id, - 2000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - // note we use the same did as bidder 1, on a different account - generate_did_from_account(BUYER_4), - InvestorType::Retail, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )); - }); - - // total contributions with same DID above 2k CT (20k USD) should fail for professionals - inst.execute(|| { - assert_ok!(Pallet::::do_contribute( - &BUYER_6, - project_id, - 1800 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(BUYER_6), - InvestorType::Professional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::do_contribute( - &BUYER_7, - project_id, - 201 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - // note we use the same did as bidder 1, on a different account - generate_did_from_account(BUYER_6), - InvestorType::Professional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - Error::::TooHigh - ); - }); - // bidding 2k total works - inst.execute(|| { - assert_ok!(Pallet::::do_contribute( - &BUYER_7, - project_id, - 200 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - // note we use the same did as bidder 1, on a different account - generate_did_from_account(BUYER_6), - InvestorType::Professional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )); - }); - - // total contributions with same DID above 5k CT (50 USD) should fail for institutionals - inst.execute(|| { - assert_ok!(Pallet::::do_contribute( - &BUYER_8, - project_id, - 4690 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - generate_did_from_account(BUYER_8), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )); - }); - inst.execute(|| { - assert_noop!( - Pallet::::do_contribute( - &BUYER_9, - project_id, - 311 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - // note we use the same did as bidder 3, on a different account - generate_did_from_account(BUYER_8), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - Error::::TooHigh - ); - }); - // bidding 5k total works - inst.execute(|| { - assert_ok!(Pallet::::do_contribute( - &BUYER_9, - project_id, - 310 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - // note we use the same did as bidder 3, on a different account - generate_did_from_account(BUYER_8), - InvestorType::Institutional, - project_metadata.clone().policy_ipfs_cid.unwrap(), - )); - }); - } - - #[test] - fn insufficient_funds() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - vec![], - ); - - let jwt = get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ); - let contribution = ContributionParams::new(BUYER_1, 1_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - // 1 unit less native asset than needed - let plmc_funding = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_funding.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - inst.execute(|| Balances::burn_from(&BUYER_1, 1, Precision::BestEffort, Fortitude::Force)).unwrap(); - - let foreign_funding = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - inst.mint_foreign_asset_to(foreign_funding.clone()); - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_1), - jwt.clone(), - project_id, - 1_000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::ParticipantNotEnoughFunds - ); - }); - - // 1 unit less funding asset than needed - let plmc_funding = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); - let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_funding.clone()); - inst.mint_plmc_to(plmc_existential_deposits.clone()); - let foreign_funding = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); - - inst.execute(|| ForeignAssets::set_balance(AcceptedFundingAsset::USDT.to_assethub_id(), &BUYER_1, 0)); - inst.mint_foreign_asset_to(foreign_funding.clone()); - - inst.execute(|| { - ForeignAssets::burn_from( - AcceptedFundingAsset::USDT.to_assethub_id(), - &BUYER_1, - 100, - Precision::BestEffort, - Fortitude::Force, - ) - }) - .unwrap(); - - inst.execute(|| { - assert_noop!( - Pallet::::contribute( - RuntimeOrigin::signed(BUYER_1), - jwt, - project_id, - 1_000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT, - ), - Error::::ParticipantNotEnoughFunds - ); - }); - } - - #[test] - fn called_outside_remainder_round() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_4, - None, - default_evaluations(), - default_bids(), - ); - - inst.execute(|| { - assert_noop!( - PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_1), - get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - project_metadata.clone().policy_ipfs_cid.unwrap() - ), - project_id, - 1000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT - ), - Error::::IncorrectRound - ); - }); - } - - #[test] - fn contribute_with_unaccepted_currencies() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let mut project_metadata_usdt = default_project_metadata(ISSUER_2); - project_metadata_usdt.participation_currencies = vec![AcceptedFundingAsset::USDT].try_into().unwrap(); - - let mut project_metadata_usdc = default_project_metadata(ISSUER_3); - project_metadata_usdc.participation_currencies = vec![AcceptedFundingAsset::USDC].try_into().unwrap(); - - let evaluations = default_evaluations(); - - let usdt_bids = default_bids() - .into_iter() - .map(|mut b| { - b.asset = AcceptedFundingAsset::USDT; - b - }) - .collect::>(); - - let usdc_bids = default_bids() - .into_iter() - .map(|mut b| { - b.asset = AcceptedFundingAsset::USDC; - b - }) - .collect::>(); - - let usdt_contribution = ContributionParams::new(BUYER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - let usdc_contribution = ContributionParams::new(BUYER_2, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); - let dot_contribution = ContributionParams::new(BUYER_3, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); - - let project_id_usdc = inst.create_remainder_contributing_project( - project_metadata_usdc, - ISSUER_3, - None, - evaluations.clone(), - usdc_bids, - vec![], - ); - assert_err!( - inst.contribute_for_users(project_id_usdc, vec![usdt_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - - let project_id_usdt = inst.create_remainder_contributing_project( - project_metadata_usdt, - ISSUER_2, - None, - evaluations.clone(), - usdt_bids, - vec![], - ); - assert_err!( - inst.contribute_for_users(project_id_usdt, vec![usdc_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - assert_err!( - inst.contribute_for_users(project_id_usdt, vec![dot_contribution.clone()]), - Error::::FundingAssetNotAccepted - ); - } - - #[test] - fn cannot_use_evaluation_bond_on_another_project_contribution() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata_1 = default_project_metadata(ISSUER_1); - let project_metadata_2 = default_project_metadata(ISSUER_2); - - let mut evaluations_1 = default_evaluations(); - let evaluations_2 = default_evaluations(); - - let evaluator_contributor = 69; - let evaluation_amount = 420 * USD_UNIT; - let evaluator_contribution = - ContributionParams::new(evaluator_contributor, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); - evaluations_1.push((evaluator_contributor, evaluation_amount).into()); - - let _project_id_1 = inst.create_remainder_contributing_project( - project_metadata_1.clone(), - ISSUER_1, - None, - evaluations_1, - default_bids(), - vec![], - ); - let project_id_2 = inst.create_remainder_contributing_project( - project_metadata_2.clone(), - ISSUER_2, - None, - evaluations_2, - default_bids(), - vec![], - ); - - let wap = inst.get_project_details(project_id_2).weighted_average_price.unwrap(); - - // Necessary Mints - let already_bonded_plmc = inst - .calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount).into()], false)[0] - .plmc_amount; - let usable_evaluation_plmc = - already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; - let necessary_plmc_for_contribution = - inst.calculate_contributed_plmc_spent(vec![evaluator_contribution.clone()], wap, false)[0].plmc_amount; - let necessary_usdt_for_contribution = - inst.calculate_contributed_funding_asset_spent(vec![evaluator_contribution.clone()], wap); - inst.mint_plmc_to(vec![UserToPLMCBalance::new( - evaluator_contributor, - necessary_plmc_for_contribution - usable_evaluation_plmc, - )]); - inst.mint_foreign_asset_to(necessary_usdt_for_contribution); - - inst.execute(|| { - assert_noop!( - PolimecFunding::contribute( - RuntimeOrigin::signed(evaluator_contributor), - get_mock_jwt_with_cid( - evaluator_contributor, - InvestorType::Retail, - generate_did_from_account(evaluator_contributor), - project_metadata_2.clone().policy_ipfs_cid.unwrap(), - ), - project_id_2, - evaluator_contribution.amount, - evaluator_contribution.multiplier, - evaluator_contribution.asset - ), - Error::::ParticipantNotEnoughFunds - ); - }); - } - - #[test] - fn wrong_policy_on_jwt() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - vec![], - ); - - inst.execute(|| { - assert_noop!( - PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_1), - get_mock_jwt_with_cid( - BUYER_1, - InvestorType::Retail, - generate_did_from_account(BUYER_1), - "wrong_cid".as_bytes().to_vec().try_into().unwrap() - ), - project_id, - 5000 * CT_UNIT, - 1u8.try_into().unwrap(), - AcceptedFundingAsset::USDT - ), - Error::::PolicyMismatch - ); - }); - } - } -} +// use super::*; +// // use crate::instantiator::async_features::create_multiple_projects_at; +// use frame_support::{dispatch::DispatchResultWithPostInfo, traits::fungibles::metadata::Inspect}; +// use sp_runtime::bounded_vec; +// use std::collections::HashSet; +// +// #[cfg(test)] +// mod round_flow { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn remainder_round_works() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let _ = inst.create_finished_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// } +// +// #[test] +// fn remainder_round_ends_on_all_ct_sold_exact() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_remainder_contributing_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// ); +// const BOB: AccountId = 808; +// +// let remaining_ct = inst.get_project_details(project_id).remaining_contribution_tokens; +// let ct_price = inst.get_project_details(project_id).weighted_average_price.expect("CT Price should exist"); +// +// let contributions = vec![ContributionParams::new(BOB, remaining_ct, 1u8, AcceptedFundingAsset::USDT)]; +// let plmc_fundings = inst.calculate_contributed_plmc_spent(contributions.clone(), ct_price, false); +// let plmc_existential_deposits = contributions.accounts().existential_deposits(); +// let foreign_asset_fundings = +// inst.calculate_contributed_funding_asset_spent(contributions.clone(), ct_price); +// +// inst.mint_plmc_to(plmc_fundings.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// inst.mint_foreign_asset_to(foreign_asset_fundings.clone()); +// +// // Buy remaining CTs +// inst.contribute_for_users(project_id, contributions) +// .expect("The Buyer should be able to buy the exact amount of remaining CTs"); +// inst.advance_time(2u64).unwrap(); +// +// // Check remaining CTs is 0 +// assert_eq!( +// inst.get_project_details(project_id).remaining_contribution_tokens, +// 0, +// "There are still remaining CTs" +// ); +// +// // Check project is in FundingEnded state +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// +// inst.do_free_plmc_assertions(plmc_existential_deposits); +// inst.do_free_foreign_asset_assertions(vec![UserToForeignAssets::::new( +// BOB, +// 0_u128, +// AcceptedFundingAsset::USDT.to_assethub_id(), +// )]); +// inst.do_reserved_plmc_assertions( +// vec![plmc_fundings[0].clone()], +// HoldReason::Participation(project_id).into(), +// ); +// inst.do_contribution_transferred_foreign_asset_assertions(foreign_asset_fundings, project_id); +// } +// +// #[test] +// fn round_has_total_ct_allocation_minus_auction_sold() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let evaluations = default_evaluations(); +// let bids = default_bids(); +// +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// evaluations.clone(), +// bids.clone(), +// vec![], +// ); +// let project_details = inst.get_project_details(project_id); +// let bid_ct_sold: BalanceOf = inst.execute(|| { +// Bids::::iter_prefix_values((project_id,)) +// .fold(Zero::zero(), |acc, bid| acc + bid.final_ct_amount) +// }); +// assert_eq!( +// project_details.remaining_contribution_tokens, +// project_metadata.total_allocation_size - bid_ct_sold +// ); +// +// let contributions = vec![(BUYER_1, project_details.remaining_contribution_tokens).into()]; +// +// let plmc_contribution_funding = inst.calculate_contributed_plmc_spent( +// contributions.clone(), +// project_details.weighted_average_price.unwrap(), +// false, +// ); +// let plmc_existential_deposits = plmc_contribution_funding.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_contribution_funding.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// let foreign_asset_contribution_funding = inst.calculate_contributed_funding_asset_spent( +// contributions.clone(), +// project_details.weighted_average_price.unwrap(), +// ); +// inst.mint_foreign_asset_to(foreign_asset_contribution_funding.clone()); +// +// inst.contribute_for_users(project_id, contributions).unwrap(); +// +// assert_eq!(inst.get_project_details(project_id).remaining_contribution_tokens, 0); +// } +// +// #[test] +// fn different_decimals_ct_works_as_expected() { +// // Setup some base values to compare different decimals +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let ed = inst.get_ed(); +// let default_project_metadata = default_project_metadata(ISSUER_1); +// let original_decimal_aware_price = default_project_metadata.minimum_price; +// let original_price = ::PriceProvider::convert_back_to_normal_price( +// original_decimal_aware_price, +// USD_DECIMALS, +// default_project_metadata.token_information.decimals, +// ) +// .unwrap(); +// let usable_plmc_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// PLMC_FOREIGN_ID, +// USD_DECIMALS, +// PLMC_DECIMALS, +// ) +// .unwrap() +// }); +// let usdt_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::USDT.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::USDT.to_assethub_id()), +// ) +// .unwrap() +// }); +// let usdc_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::USDC.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::USDC.to_assethub_id()), +// ) +// .unwrap() +// }); +// let dot_price = inst.execute(|| { +// ::PriceProvider::get_decimals_aware_price( +// AcceptedFundingAsset::DOT.to_assethub_id(), +// USD_DECIMALS, +// ForeignAssets::decimals(AcceptedFundingAsset::DOT.to_assethub_id()), +// ) +// .unwrap() +// }); +// +// let mut funding_assets_cycle = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] +// .into_iter() +// .cycle(); +// +// let mut total_fundings_ct = Vec::new(); +// let mut total_fundings_usd = Vec::new(); +// let mut total_fundings_plmc = Vec::new(); +// +// let mut decimal_test = |decimals: u8| { +// let funding_asset = funding_assets_cycle.next().unwrap(); +// let funding_asset_usd_price = match funding_asset { +// AcceptedFundingAsset::USDT => usdt_price, +// AcceptedFundingAsset::USDC => usdc_price, +// AcceptedFundingAsset::DOT => dot_price, +// }; +// +// let mut project_metadata = default_project_metadata.clone(); +// project_metadata.token_information.decimals = decimals; +// project_metadata.minimum_price = +// ::PriceProvider::calculate_decimals_aware_price( +// original_price, +// USD_DECIMALS, +// decimals, +// ) +// .unwrap(); +// +// project_metadata.total_allocation_size = 1_000_000 * 10u128.pow(decimals as u32); +// project_metadata.mainnet_token_max_supply = project_metadata.total_allocation_size; +// project_metadata.participation_currencies = bounded_vec!(funding_asset); +// +// let issuer: AccountIdOf = (10_000 + inst.get_new_nonce()).try_into().unwrap(); +// let evaluations = inst.generate_successful_evaluations( +// project_metadata.clone(), +// default_evaluators(), +// default_weights(), +// ); +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// issuer, +// None, +// evaluations, +// vec![], +// vec![], +// ); +// +// let total_funding_ct = project_metadata.total_allocation_size; +// let total_funding_usd = project_metadata.minimum_price.saturating_mul_int(total_funding_ct); +// let total_funding_plmc = usable_plmc_price.reciprocal().unwrap().saturating_mul_int(total_funding_usd); +// let total_funding_funding_asset = +// funding_asset_usd_price.reciprocal().unwrap().saturating_mul_int(total_funding_usd); +// +// total_fundings_ct.push(total_funding_ct); +// total_fundings_usd.push(total_funding_usd); +// total_fundings_plmc.push(total_funding_plmc); +// +// // Every project should want to raise 10MM USD +// assert_eq!(total_funding_usd, 10_000_000 * USD_UNIT); +// +// // Every project should produce the same PLMC bond when having the full funding at multiplier 1. +// assert_close_enough!(total_funding_plmc, 1_190_476 * PLMC, Perquintill::from_float(0.999)); +// +// // Every project should have a different amount of CTs to raise, depending on their decimals +// assert_eq!(total_funding_ct, 1_000_000 * 10u128.pow(decimals as u32)); +// +// // Buying all the remaining tokens. This is a fixed USD value, but the extrinsic amount depends on CT decimals. +// inst.mint_plmc_to(vec![UserToPLMCBalance::new(BUYER_1, total_funding_plmc + ed)]); +// inst.mint_foreign_asset_to(vec![UserToForeignAssets::new( +// BUYER_1, +// total_funding_funding_asset, +// funding_asset.to_assethub_id(), +// )]); +// +// assert_ok!(inst.execute(|| PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_1), +// get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// total_funding_ct, +// 1u8.try_into().unwrap(), +// funding_asset, +// ))); +// +// // the remaining tokens should be zero +// assert_eq!(inst.get_project_details(project_id).remaining_contribution_tokens, 0); +// +// // We can successfully finish the project +// inst.finish_funding(project_id, None).unwrap(); +// }; +// +// for decimals in 6..=18 { +// decimal_test(decimals); +// } +// +// // Since we use the same original price and allocation size and adjust for decimals, +// // the USD and PLMC amounts should be the same +// assert!(total_fundings_usd.iter().all(|x| *x == total_fundings_usd[0])); +// assert!(total_fundings_plmc.iter().all(|x| *x == total_fundings_plmc[0])); +// +// // CT amounts however should be different from each other +// let mut hash_set_1 = HashSet::new(); +// for amount in total_fundings_ct { +// assert!(!hash_set_1.contains(&amount)); +// hash_set_1.insert(amount); +// } +// } +// } +// } +// +// #[cfg(test)] +// mod contribute_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// use frame_support::traits::fungible::InspectFreeze; +// use pallet_balances::AccountData; +// +// #[test] +// fn evaluation_bond_counts_towards_contribution() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// +// const BOB: AccountId = 42069; +// const CARL: AccountId = 420691; +// let mut evaluations = default_evaluations(); +// let bob_evaluation: UserToUSDBalance = (BOB, 1337 * USD_UNIT).into(); +// let carl_evaluation: UserToUSDBalance = (CARL, 1337 * USD_UNIT).into(); +// evaluations.push(bob_evaluation.clone()); +// evaluations.push(carl_evaluation.clone()); +// +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// evaluations, +// default_bids(), +// vec![], +// ); +// let ct_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let plmc_price = ::PriceProvider::get_decimals_aware_price( +// PLMC_FOREIGN_ID, +// USD_DECIMALS, +// PLMC_DECIMALS, +// ) +// .unwrap(); +// +// let evaluation_plmc_bond = +// inst.execute(|| Balances::balance_on_hold(&HoldReason::Evaluation(project_id).into(), &BOB)); +// let slashable_plmc = ::EvaluatorSlash::get() * evaluation_plmc_bond; +// let usable_plmc = evaluation_plmc_bond - slashable_plmc; +// +// let usable_usd = plmc_price.checked_mul_int(usable_plmc).unwrap(); +// let slashable_usd = plmc_price.checked_mul_int(slashable_plmc).unwrap(); +// +// let usable_ct = ct_price.reciprocal().unwrap().saturating_mul_int(usable_usd); +// let slashable_ct = ct_price.reciprocal().unwrap().saturating_mul_int(slashable_usd); +// +// // Can't contribute with only the evaluation bond +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BOB), +// get_mock_jwt_with_cid( +// BOB, +// InvestorType::Retail, +// generate_did_from_account(BOB), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// usable_ct + slashable_ct, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// +// // Can partially use the usable evaluation bond (half in this case) +// let contribution_usdt = +// inst.calculate_contributed_funding_asset_spent(vec![(BOB, usable_ct / 2).into()], ct_price); +// inst.mint_foreign_asset_to(contribution_usdt.clone()); +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(BOB), +// get_mock_jwt_with_cid( +// BOB, +// InvestorType::Retail, +// generate_did_from_account(BOB), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// usable_ct / 2, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// +// // Can use the full evaluation bond +// let contribution_usdt = +// inst.calculate_contributed_funding_asset_spent(vec![(CARL, usable_ct).into()], ct_price); +// inst.mint_foreign_asset_to(contribution_usdt.clone()); +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(CARL), +// get_mock_jwt_with_cid( +// CARL, +// InvestorType::Retail, +// generate_did_from_account(CARL), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// usable_ct, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// } +// +// #[test] +// fn evaluation_bond_used_on_failed_bid_can_be_reused_on_contribution() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let bob = 42069; +// let project_metadata = default_project_metadata(ISSUER_1); +// // An evaluator that did a bid but it was not accepted at the end of the auction, can use that PLMC for contributing +// let mut evaluations = default_evaluations(); +// let bob_evaluation = (bob, 1337 * USD_UNIT).into(); +// evaluations.push(bob_evaluation); +// +// let bids = default_bids(); +// let bob_bid: BidParams = (bob, 1337 * CT_UNIT).into(); +// let all_bids = bids.iter().chain(vec![bob_bid.clone()].iter()).cloned().collect_vec(); +// +// let project_id = +// inst.create_auctioning_project(default_project_metadata(ISSUER_2), ISSUER_2, None, evaluations); +// +// let evaluation_plmc_bond = +// inst.execute(|| Balances::balance_on_hold(&HoldReason::Evaluation(project_id).into(), &bob)); +// let slashable_plmc_bond = ::EvaluatorSlash::get() * evaluation_plmc_bond; +// let usable_plmc_bond = evaluation_plmc_bond - slashable_plmc_bond; +// +// let bids_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &all_bids, +// project_metadata.clone(), +// None, +// true, +// ); +// inst.mint_plmc_to(bids_plmc.clone()); +// +// let bids_foreign = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &all_bids, +// project_metadata.clone(), +// None, +// ); +// inst.mint_foreign_asset_to(bids_foreign.clone()); +// +// inst.bid_for_users(project_id, bids).unwrap(); +// +// let auction_end = ::AuctionOpeningDuration::get() + +// ::AuctionClosingDuration::get(); +// inst.advance_time(auction_end - 1).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Auction); +// inst.bid_for_users(project_id, vec![bob_bid]).unwrap(); +// +// inst.start_community_funding(project_id).unwrap(); +// assert!(matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(..))); +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// +// let plmc_price = ::PriceProvider::get_decimals_aware_price( +// PLMC_FOREIGN_ID, +// USD_DECIMALS, +// PLMC_DECIMALS, +// ) +// .unwrap(); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let usable_usd = plmc_price.saturating_mul_int(usable_plmc_bond); +// let usable_ct = wap.reciprocal().unwrap().saturating_mul_int(usable_usd); +// +// let bob_contribution = (bob, 1337 * CT_UNIT).into(); +// let contribution_usdt = inst.calculate_contributed_funding_asset_spent(vec![bob_contribution], wap); +// inst.mint_foreign_asset_to(contribution_usdt.clone()); +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(bob), +// get_mock_jwt_with_cid( +// bob, +// InvestorType::Retail, +// generate_did_from_account(bob), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// usable_ct, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// } +// +// #[test] +// fn contribute_with_multiple_currencies() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let mut project_metadata_all = default_project_metadata(ISSUER_1); +// project_metadata_all.participation_currencies = +// vec![AcceptedFundingAsset::USDT, AcceptedFundingAsset::USDC, AcceptedFundingAsset::DOT] +// .try_into() +// .unwrap(); +// +// let mut project_metadata_usdt = default_project_metadata(ISSUER_2); +// project_metadata_usdt.participation_currencies = vec![AcceptedFundingAsset::USDT].try_into().unwrap(); +// +// let mut project_metadata_usdc = default_project_metadata(ISSUER_3); +// project_metadata_usdc.participation_currencies = vec![AcceptedFundingAsset::USDC].try_into().unwrap(); +// +// let mut project_metadata_dot = default_project_metadata(ISSUER_4); +// project_metadata_dot.participation_currencies = vec![AcceptedFundingAsset::DOT].try_into().unwrap(); +// +// let evaluations = default_evaluations(); +// +// let usdt_bids = default_bids() +// .into_iter() +// .map(|mut b| { +// b.asset = AcceptedFundingAsset::USDT; +// b +// }) +// .collect::>(); +// +// let usdc_bids = default_bids() +// .into_iter() +// .map(|mut b| { +// b.asset = AcceptedFundingAsset::USDC; +// b +// }) +// .collect::>(); +// +// let dot_bids = default_bids() +// .into_iter() +// .map(|mut b| { +// b.asset = AcceptedFundingAsset::DOT; +// b +// }) +// .collect::>(); +// +// let usdt_contribution = ContributionParams::new(BUYER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let usdc_contribution = ContributionParams::new(BUYER_2, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); +// let dot_contribution = ContributionParams::new(BUYER_3, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); +// +// let project_id_all = inst.create_remainder_contributing_project( +// project_metadata_all.clone(), +// ISSUER_1, +// None, +// evaluations.clone(), +// default_bids(), +// vec![], +// ); +// let wap = inst.get_project_details(project_id_all).weighted_average_price.unwrap(); +// +// let plmc_fundings = inst.calculate_contributed_plmc_spent( +// vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()], +// wap, +// false, +// ); +// let plmc_existential_deposits = plmc_fundings.accounts().existential_deposits(); +// let plmc_all_mints = +// inst.generic_map_operation(vec![plmc_fundings, plmc_existential_deposits], MergeOperation::Add); +// inst.mint_plmc_to(plmc_all_mints.clone()); +// inst.mint_plmc_to(plmc_all_mints.clone()); +// inst.mint_plmc_to(plmc_all_mints.clone()); +// +// let usdt_fundings = inst.calculate_contributed_funding_asset_spent( +// vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()], +// wap, +// ); +// inst.mint_foreign_asset_to(usdt_fundings.clone()); +// inst.mint_foreign_asset_to(usdt_fundings.clone()); +// inst.mint_foreign_asset_to(usdt_fundings.clone()); +// +// assert_ok!(inst.contribute_for_users( +// project_id_all, +// vec![usdt_contribution.clone(), usdc_contribution.clone(), dot_contribution.clone()] +// )); +// +// let project_id_usdt = inst.create_remainder_contributing_project( +// project_metadata_usdt.clone(), +// ISSUER_2, +// None, +// evaluations.clone(), +// usdt_bids, +// vec![], +// ); +// +// assert_ok!(inst.contribute_for_users(project_id_usdt, vec![usdt_contribution.clone()])); +// assert_err!( +// inst.contribute_for_users(project_id_usdt, vec![usdc_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_err!( +// inst.contribute_for_users(project_id_usdt, vec![dot_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// +// let project_id_usdc = inst.create_remainder_contributing_project( +// project_metadata_usdc.clone(), +// ISSUER_3, +// None, +// evaluations.clone(), +// usdc_bids, +// vec![], +// ); +// +// assert_err!( +// inst.contribute_for_users(project_id_usdc, vec![usdt_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_ok!(inst.contribute_for_users(project_id_usdc, vec![usdc_contribution.clone()])); +// assert_err!( +// inst.contribute_for_users(project_id_usdc, vec![dot_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// +// let project_id_dot = inst.create_remainder_contributing_project( +// project_metadata_dot.clone(), +// ISSUER_4, +// None, +// evaluations.clone(), +// dot_bids, +// vec![], +// ); +// +// assert_err!( +// inst.contribute_for_users(project_id_dot, vec![usdt_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_err!( +// inst.contribute_for_users(project_id_dot, vec![usdc_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_ok!(inst.contribute_for_users(project_id_dot, vec![dot_contribution.clone()])); +// } +// +// fn test_contribution_setup( +// inst: &mut MockInstantiator, +// project_id: ProjectId, +// contributor: AccountIdOf, +// investor_type: InvestorType, +// u8_multiplier: u8, +// ) -> DispatchResultWithPostInfo { +// let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); +// let jwt = get_mock_jwt_with_cid( +// contributor.clone(), +// investor_type, +// generate_did_from_account(contributor), +// project_policy, +// ); +// let amount = 1000 * CT_UNIT; +// let multiplier = Multiplier::force_new(u8_multiplier); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// if u8_multiplier > 0 { +// let contribution = ContributionParams:: { +// contributor: contributor.clone(), +// amount, +// multiplier, +// asset: AcceptedFundingAsset::USDT, +// }; +// +// let necessary_plmc = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let plmc_existential_amounts = necessary_plmc.accounts().existential_deposits(); +// let necessary_usdt = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// +// inst.mint_plmc_to(necessary_plmc.clone()); +// inst.mint_plmc_to(plmc_existential_amounts.clone()); +// inst.mint_foreign_asset_to(necessary_usdt.clone()); +// } +// inst.execute(|| { +// Pallet::::contribute( +// RuntimeOrigin::signed(contributor), +// jwt, +// project_id, +// amount, +// multiplier, +// AcceptedFundingAsset::USDT, +// ) +// }) +// } +// +// #[test] +// fn non_retail_multiplier_limits() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut project_metadata = default_project_metadata(ISSUER_1); +// project_metadata.mainnet_token_max_supply = 80_000_000 * CT_UNIT; +// project_metadata.total_allocation_size = 10_000_000 * CT_UNIT; +// project_metadata.bidding_ticket_sizes = BiddingTicketSizes { +// professional: TicketSize::new(5000 * USD_UNIT, None), +// institutional: TicketSize::new(5000 * USD_UNIT, None), +// phantom: Default::default(), +// }; +// project_metadata.contributing_ticket_sizes = ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, None), +// professional: TicketSize::new(USD_UNIT, None), +// institutional: TicketSize::new(USD_UNIT, None), +// phantom: Default::default(), +// }; +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); +// let bids = inst.generate_bids_from_total_ct_percent( +// project_metadata.clone(), +// 50, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// evaluations, +// bids, +// vec![], +// ); +// +// // Professional contributions: 0x multiplier should fail +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Professional, 0), +// Error::::ForbiddenMultiplier +// ); +// // Professional contributions: 1 - 10x multiplier should work +// for multiplier in 1..=10u8 { +// assert_ok!(test_contribution_setup( +// &mut inst, +// project_id, +// BUYER_1, +// InvestorType::Professional, +// multiplier +// )); +// } +// // Professional contributions: >=11x multiplier should fail +// for multiplier in 11..=50u8 { +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Professional, multiplier), +// Error::::ForbiddenMultiplier +// ); +// } +// +// // Institutional contributions: 0x multiplier should fail +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_2, InvestorType::Institutional, 0), +// Error::::ForbiddenMultiplier +// ); +// // Institutional contributions: 1 - 25x multiplier should work +// for multiplier in 1..=25u8 { +// assert_ok!(test_contribution_setup( +// &mut inst, +// project_id, +// BUYER_2, +// InvestorType::Institutional, +// multiplier +// )); +// } +// // Institutional contributions: >=26x multiplier should fail +// for multiplier in 26..=50u8 { +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_2, InvestorType::Institutional, multiplier), +// Error::::ForbiddenMultiplier +// ); +// } +// } +// +// #[test] +// fn retail_multiplier_limits() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let mut issuer: AccountId = 6969420; +// +// let mut create_project = |inst: &mut MockInstantiator| { +// issuer += 1; +// inst.create_remainder_contributing_project( +// default_project_metadata(issuer), +// issuer, +// None, +// default_evaluations(), +// default_bids(), +// vec![], +// ) +// }; +// +// let max_allowed_multipliers_map = vec![(2, 1), (4, 2), (9, 4), (24, 7), (25, 10)]; +// +// let mut previous_projects_created = 0; +// for (projects_participated_amount, max_allowed_multiplier) in max_allowed_multipliers_map { +// (previous_projects_created..projects_participated_amount - 1).for_each(|_| { +// let project_id = create_project(&mut inst); +// assert_ok!(test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, 1)); +// }); +// +// let project_id = create_project(&mut inst); +// previous_projects_created = projects_participated_amount; +// +// // 0x multiplier should fail +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, 0), +// Error::::ForbiddenMultiplier +// ); +// +// // Multipliers that should work +// for multiplier in 1..=max_allowed_multiplier { +// assert_ok!(test_contribution_setup( +// &mut inst, +// project_id, +// BUYER_1, +// InvestorType::Retail, +// multiplier +// )); +// } +// +// // Multipliers that should NOT work +// for multiplier in max_allowed_multiplier + 1..=50 { +// assert_err!( +// test_contribution_setup(&mut inst, project_id, BUYER_1, InvestorType::Retail, multiplier), +// Error::::ForbiddenMultiplier +// ); +// } +// } +// } +// +// #[test] +// fn did_with_winning_bid_can_contribute() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let mut evaluations = default_evaluations(); +// evaluations.push((BIDDER_4, 1337 * USD_UNIT).into()); +// +// let successful_bids = vec![ +// BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_2, 100_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ]; +// let failing_bids_after_random_end = +// vec![(BIDDER_3, 25_000 * CT_UNIT).into(), (BIDDER_4, 25_000 * CT_UNIT).into()]; +// // This bids should fill the first bucket. +// let failing_bids_sold_out = +// vec![(BIDDER_5, 250_000 * CT_UNIT).into(), (BIDDER_6, 250_000 * CT_UNIT).into()]; +// +// let all_bids = failing_bids_sold_out +// .iter() +// .chain(successful_bids.iter()) +// .chain(failing_bids_after_random_end.iter()) +// .cloned() +// .collect_vec(); +// +// let project_id = +// inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); +// +// let plmc_fundings = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &all_bids.clone(), +// project_metadata.clone(), +// None, +// true, +// ); +// inst.mint_plmc_to(plmc_fundings.clone()); +// +// let foreign_funding = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &all_bids.clone(), +// project_metadata.clone(), +// None, +// ); +// inst.mint_foreign_asset_to(foreign_funding.clone()); +// +// inst.bid_for_users(project_id, failing_bids_sold_out).unwrap(); +// inst.bid_for_users(project_id, successful_bids).unwrap(); +// inst.advance_time( +// ::AuctionOpeningDuration::get() + +// ::AuctionClosingDuration::get(), +// ) +// .unwrap(); +// inst.bid_for_users(project_id, failing_bids_after_random_end).unwrap(); +// inst.advance_time(2).unwrap(); +// assert!(matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(..))); +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// +// // Some low amount of plmc and usdt to cover a purchase of 10CTs. +// let plmc_mints = vec![ +// (BIDDER_3, 42069 * PLMC).into(), +// (BIDDER_4, 42069 * PLMC).into(), +// (BIDDER_5, 42069 * PLMC).into(), +// (BIDDER_6, 42069 * PLMC).into(), +// (BUYER_3, 42069 * PLMC).into(), +// (BUYER_4, 42069 * PLMC).into(), +// (BUYER_5, 42069 * PLMC).into(), +// (BUYER_6, 42069 * PLMC).into(), +// ]; +// inst.mint_plmc_to(plmc_mints); +// let usdt_mints = vec![ +// (BIDDER_3, 42069 * CT_UNIT).into(), +// (BIDDER_4, 42069 * CT_UNIT).into(), +// (BIDDER_5, 42069 * CT_UNIT).into(), +// (BIDDER_6, 42069 * CT_UNIT).into(), +// (BUYER_3, 42069 * CT_UNIT).into(), +// (BUYER_4, 42069 * CT_UNIT).into(), +// (BUYER_5, 42069 * CT_UNIT).into(), +// (BUYER_6, 42069 * CT_UNIT).into(), +// ]; +// inst.mint_foreign_asset_to(usdt_mints); +// +// let mut bid_should_succeed = |account, investor_type, did_acc| { +// inst.execute(|| { +// assert_ok!(Pallet::::contribute( +// RuntimeOrigin::signed(account), +// get_mock_jwt_with_cid( +// account, +// investor_type, +// generate_did_from_account(did_acc), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// 10 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// )); +// }); +// }; +// +// // Bidder 3 has a losing bid due to bidding after the random end. His did should be able to contribute regardless of what investor type +// // or account he uses to sign the transaction +// bid_should_succeed(BIDDER_3, InvestorType::Institutional, BIDDER_3); +// bid_should_succeed(BUYER_3, InvestorType::Institutional, BIDDER_3); +// bid_should_succeed(BIDDER_3, InvestorType::Professional, BIDDER_3); +// bid_should_succeed(BUYER_3, InvestorType::Professional, BIDDER_3); +// bid_should_succeed(BIDDER_3, InvestorType::Retail, BIDDER_3); +// bid_should_succeed(BUYER_3, InvestorType::Retail, BIDDER_3); +// +// // Bidder 4 has a losing bid due to bidding after the random end, and he was also an evaluator. Same conditions as before should apply. +// bid_should_succeed(BIDDER_4, InvestorType::Institutional, BIDDER_4); +// bid_should_succeed(BUYER_4, InvestorType::Institutional, BIDDER_4); +// bid_should_succeed(BIDDER_4, InvestorType::Professional, BIDDER_4); +// bid_should_succeed(BUYER_4, InvestorType::Professional, BIDDER_4); +// bid_should_succeed(BIDDER_4, InvestorType::Retail, BIDDER_4); +// bid_should_succeed(BUYER_4, InvestorType::Retail, BIDDER_4); +// +// // Bidder 5 has a losing bid due to CTs being sold out at his price point. Same conditions as before should apply. +// bid_should_succeed(BIDDER_5, InvestorType::Institutional, BIDDER_5); +// bid_should_succeed(BUYER_5, InvestorType::Institutional, BIDDER_5); +// bid_should_succeed(BIDDER_5, InvestorType::Professional, BIDDER_5); +// bid_should_succeed(BUYER_5, InvestorType::Professional, BIDDER_5); +// bid_should_succeed(BIDDER_5, InvestorType::Retail, BIDDER_5); +// bid_should_succeed(BUYER_5, InvestorType::Retail, BIDDER_5); +// +// // Bidder 6 has a losing bid due to CTs being sold out at his price point, and he was also an evaluator. Same conditions as before should apply. +// bid_should_succeed(BIDDER_6, InvestorType::Institutional, BIDDER_6); +// bid_should_succeed(BUYER_6, InvestorType::Institutional, BIDDER_6); +// bid_should_succeed(BIDDER_6, InvestorType::Professional, BIDDER_6); +// bid_should_succeed(BUYER_6, InvestorType::Professional, BIDDER_6); +// bid_should_succeed(BIDDER_6, InvestorType::Retail, BIDDER_6); +// bid_should_succeed(BUYER_6, InvestorType::Retail, BIDDER_6); +// } +// +// #[test] +// fn can_contribute_with_frozen_tokens_funding_failed() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// issuer, +// None, +// default_evaluations(), +// vec![], +// vec![], +// ); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let contribution = ContributionParams::new(BUYER_4, 500 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let frozen_amount = plmc_required[0].plmc_amount; +// let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); +// +// inst.mint_plmc_to(plmc_existential_deposits); +// inst.mint_plmc_to(plmc_required.clone()); +// +// inst.execute(|| { +// mock::Balances::set_freeze(&(), &BUYER_4, plmc_required[0].plmc_amount).unwrap(); +// }); +// +// let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// inst.mint_foreign_asset_to(usdt_required); +// +// inst.execute(|| { +// assert_noop!( +// Balances::transfer_allow_death(RuntimeOrigin::signed(BUYER_4), ISSUER_1, frozen_amount,), +// TokenError::Frozen +// ); +// }); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_4), +// get_mock_jwt_with_cid( +// BUYER_4, +// InvestorType::Institutional, +// generate_did_from_account(BUYER_4), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// contribution.amount, +// contribution.multiplier, +// contribution.asset +// )); +// }); +// +// inst.finish_funding(project_id, None).unwrap(); +// +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// let account_data = inst.execute(|| System::account(&BUYER_4)).data; +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// let expected_account_data = AccountData { +// free: inst.get_ed(), +// reserved: frozen_amount, +// frozen: frozen_amount, +// flags: Default::default(), +// }; +// assert_eq!(account_data, expected_account_data); +// +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// inst.execute(|| { +// PolimecFunding::settle_failed_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0) +// .unwrap(); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Evaluation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// let account_data = inst.execute(|| System::account(&BUYER_4)).data; +// +// assert_eq!(free_balance, inst.get_ed() + frozen_amount); +// assert_eq!(bid_held_balance, Zero::zero()); +// assert_eq!(frozen_balance, frozen_amount); +// let expected_account_data = AccountData { +// free: inst.get_ed() + frozen_amount, +// reserved: Zero::zero(), +// frozen: frozen_amount, +// flags: Default::default(), +// }; +// assert_eq!(account_data, expected_account_data); +// assert_eq!(account_data.frozen, account_data.free - inst.get_ed()); +// } +// +// #[test] +// fn can_contribute_with_frozen_tokens_funding_success() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// issuer, +// None, +// default_evaluations(), +// default_bids(), +// vec![], +// ); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let contribution = ContributionParams::new(BUYER_4, 500 * CT_UNIT, 5u8, AcceptedFundingAsset::USDT); +// let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let frozen_amount = plmc_required[0].plmc_amount; +// let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); +// +// inst.mint_plmc_to(plmc_existential_deposits); +// inst.mint_plmc_to(plmc_required.clone()); +// +// inst.execute(|| { +// mock::Balances::set_freeze(&(), &BUYER_4, plmc_required[0].plmc_amount).unwrap(); +// }); +// +// let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// inst.mint_foreign_asset_to(usdt_required); +// +// inst.execute(|| { +// assert_noop!( +// Balances::transfer_allow_death(RuntimeOrigin::signed(BUYER_4), ISSUER_1, frozen_amount,), +// TokenError::Frozen +// ); +// }); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_4), +// get_mock_jwt_with_cid( +// BUYER_4, +// InvestorType::Institutional, +// generate_did_from_account(BUYER_4), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// contribution.amount, +// contribution.multiplier, +// contribution.asset +// )); +// }); +// +// inst.finish_funding(project_id, None).unwrap(); +// +// let decision_block = inst +// .get_update_block(project_id, &UpdateType::ProjectDecision(FundingOutcomeDecision::AcceptFunding)) +// .unwrap(); +// inst.jump_to_block(decision_block); +// +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// let account_data = inst.execute(|| System::account(&BUYER_4)).data; +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// let expected_account_data = AccountData { +// free: inst.get_ed(), +// reserved: frozen_amount, +// frozen: frozen_amount, +// flags: Default::default(), +// }; +// assert_eq!(account_data, expected_account_data); +// +// inst.execute(|| { +// PolimecFunding::settle_successful_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0) +// .unwrap(); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// let account_data = inst.execute(|| System::account(&BUYER_4)).data; +// +// assert_eq!(free_balance, inst.get_ed()); +// assert_eq!(bid_held_balance, frozen_amount); +// assert_eq!(frozen_balance, frozen_amount); +// let expected_account_data = AccountData { +// free: inst.get_ed(), +// reserved: frozen_amount, +// frozen: frozen_amount, +// flags: Default::default(), +// }; +// assert_eq!(account_data, expected_account_data); +// +// let vest_duration = +// MultiplierOf::::new(5u8).unwrap().calculate_vesting_duration::(); +// let now = inst.current_block(); +// inst.jump_to_block(now + vest_duration + 1u64); +// inst.execute(|| { +// assert_ok!(mock::LinearRelease::vest( +// RuntimeOrigin::signed(BUYER_4), +// HoldReason::Participation(project_id).into() +// )); +// }); +// +// let free_balance = inst.get_free_plmc_balance_for(BUYER_4); +// let bid_held_balance = +// inst.get_reserved_plmc_balance_for(BUYER_4, HoldReason::Participation(project_id).into()); +// let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &BUYER_4)); +// let account_data = inst.execute(|| System::account(&BUYER_4)).data; +// +// assert_eq!(free_balance, inst.get_ed() + frozen_amount); +// assert_eq!(bid_held_balance, Zero::zero()); +// assert_eq!(frozen_balance, frozen_amount); +// let expected_account_data = AccountData { +// free: inst.get_ed() + frozen_amount, +// reserved: Zero::zero(), +// frozen: frozen_amount, +// flags: Default::default(), +// }; +// assert_eq!(account_data, expected_account_data); +// } +// +// #[test] +// fn participant_was_evaluator_and_bidder() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let issuer = ISSUER_1; +// let participant = 42069u32; +// let project_metadata = default_project_metadata(issuer); +// let mut evaluations = default_evaluations(); +// evaluations.push((participant, 100 * USD_UNIT).into()); +// let mut bids = default_bids(); +// bids.push(BidParams::new(participant, 1000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT)); +// let community_contributions = default_community_buys(); +// let mut remainder_contributions = default_remainder_buys(); +// remainder_contributions.push(ContributionParams::new( +// participant, +// 10 * CT_UNIT, +// 1u8, +// AcceptedFundingAsset::USDT, +// )); +// +// let _project_id = inst.create_finished_project( +// project_metadata.clone(), +// issuer, +// None, +// evaluations, +// bids, +// community_contributions, +// remainder_contributions, +// ); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// use frame_support::traits::{ +// fungible::Mutate, +// fungibles::Mutate as OtherMutate, +// tokens::{Fortitude, Precision}, +// }; +// +// #[test] +// fn contribution_errors_if_user_limit_is_reached() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_remainder_contributing_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// vec![], +// ); +// const CONTRIBUTOR: AccountIdOf = 420; +// +// let project_details = inst.get_project_details(project_id); +// let token_price = project_details.weighted_average_price.unwrap(); +// +// // Create a contribution vector that will reach the limit of contributions for a user-project +// let token_amount: BalanceOf = CT_UNIT; +// let range = 0..::MaxContributionsPerUser::get(); +// let contributions: Vec> = range +// .map(|_| ContributionParams::new(CONTRIBUTOR, token_amount, 1u8, AcceptedFundingAsset::USDT)) +// .collect(); +// +// let plmc_funding = inst.calculate_contributed_plmc_spent(contributions.clone(), token_price, false); +// let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); +// +// let foreign_funding = inst.calculate_contributed_funding_asset_spent(contributions.clone(), token_price); +// +// inst.mint_plmc_to(plmc_funding.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// +// inst.mint_foreign_asset_to(foreign_funding.clone()); +// +// // Reach up to the limit of contributions for a user-project +// assert!(inst.contribute_for_users(project_id, contributions).is_ok()); +// +// // Try to contribute again, but it should fail because the limit of contributions for a user-project was reached. +// let over_limit_contribution = +// ContributionParams::new(CONTRIBUTOR, token_amount, 1u8, AcceptedFundingAsset::USDT); +// assert!(inst.contribute_for_users(project_id, vec![over_limit_contribution]).is_err()); +// +// // Check that the right amount of PLMC is bonded, and funding currency is transferred +// let contributor_post_buy_plmc_balance = +// inst.execute(|| ::NativeCurrency::balance(&CONTRIBUTOR)); +// let contributor_post_buy_foreign_asset_balance = inst.execute(|| { +// ::FundingCurrency::balance( +// AcceptedFundingAsset::USDT.to_assethub_id(), +// CONTRIBUTOR, +// ) +// }); +// +// assert_eq!(contributor_post_buy_plmc_balance, inst.get_ed()); +// assert_eq!(contributor_post_buy_foreign_asset_balance, 0); +// +// let plmc_bond_stored = inst.execute(|| { +// ::NativeCurrency::balance_on_hold( +// &HoldReason::Participation(project_id.into()).into(), +// &CONTRIBUTOR, +// ) +// }); +// let foreign_asset_contributions_stored = inst.execute(|| { +// Contributions::::iter_prefix_values((project_id, CONTRIBUTOR)) +// .map(|c| c.funding_asset_amount) +// .sum::>() +// }); +// +// assert_eq!(plmc_bond_stored, inst.sum_balance_mappings(vec![plmc_funding.clone()])); +// assert_eq!(foreign_asset_contributions_stored, inst.sum_foreign_mappings(vec![foreign_funding.clone()])); +// } +// +// #[test] +// fn issuer_cannot_contribute_his_project() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// ); +// assert_err!( +// inst.execute(|| crate::Pallet::::do_contribute( +// &(&ISSUER_1 + 1), +// project_id, +// 500 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(ISSUER_1), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )), +// Error::::ParticipationToOwnProject +// ); +// } +// +// #[test] +// fn per_credential_type_ticket_size_minimums() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = ProjectMetadata { +// token_information: default_token_information(), +// mainnet_token_max_supply: 8_000_000 * CT_UNIT, +// total_allocation_size: 1_000_000 * CT_UNIT, +// auction_round_allocation_percentage: Percent::from_percent(50u8), +// minimum_price: PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(10.0), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(), +// bidding_ticket_sizes: BiddingTicketSizes { +// professional: TicketSize::new(8000 * USD_UNIT, None), +// institutional: TicketSize::new(20_000 * USD_UNIT, None), +// phantom: Default::default(), +// }, +// contributing_ticket_sizes: ContributingTicketSizes { +// retail: TicketSize::new(10 * USD_UNIT, None), +// professional: TicketSize::new(100_000 * USD_UNIT, None), +// institutional: TicketSize::new(200_000 * USD_UNIT, None), +// phantom: Default::default(), +// }, +// participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), +// funding_destination_account: ISSUER_1, +// policy_ipfs_cid: Some(ipfs_hash()), +// }; +// +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// vec![], +// ); +// +// inst.mint_plmc_to(vec![ +// (BUYER_4, 50_000 * PLMC).into(), +// (BUYER_5, 50_000 * PLMC).into(), +// (BUYER_6, 50_000 * PLMC).into(), +// ]); +// +// inst.mint_foreign_asset_to(vec![ +// (BUYER_4, 50_000 * USDT_UNIT).into(), +// (BUYER_5, 50_000 * USDT_UNIT).into(), +// (BUYER_6, 50_000 * USDT_UNIT).into(), +// ]); +// +// // contribution below 1 CT (10 USD) should fail for retail +// let jwt = get_mock_jwt_with_cid( +// BUYER_4, +// InvestorType::Retail, +// generate_did_from_account(BUYER_4), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_4), +// jwt, +// project_id, +// CT_UNIT / 2, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooLow +// ); +// }); +// // contribution below 10_000 CT (100k USD) should fail for professionals +// let jwt = get_mock_jwt_with_cid( +// BUYER_5, +// InvestorType::Professional, +// generate_did_from_account(BUYER_5), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_5), +// jwt, +// project_id, +// 9_999, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooLow +// ); +// }); +// +// // contribution below 20_000 CT (200k USD) should fail for institutionals +// let jwt = get_mock_jwt_with_cid( +// BUYER_6, +// InvestorType::Institutional, +// generate_did_from_account(BUYER_6), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_6), +// jwt, +// project_id, +// 19_999, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::TooLow +// ); +// }); +// } +// +// #[test] +// fn per_credential_type_ticket_size_maximums() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = ProjectMetadata { +// token_information: default_token_information(), +// mainnet_token_max_supply: 8_000_000 * CT_UNIT, +// total_allocation_size: 1_000_000 * CT_UNIT, +// auction_round_allocation_percentage: Percent::from_percent(50u8), +// minimum_price: PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(10.0), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(), +// bidding_ticket_sizes: BiddingTicketSizes { +// professional: TicketSize::new(5000 * USD_UNIT, None), +// institutional: TicketSize::new(5000 * USD_UNIT, None), +// phantom: Default::default(), +// }, +// contributing_ticket_sizes: ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, Some(300_000 * USD_UNIT)), +// professional: TicketSize::new(USD_UNIT, Some(20_000 * USD_UNIT)), +// institutional: TicketSize::new(USD_UNIT, Some(50_000 * USD_UNIT)), +// phantom: Default::default(), +// }, +// participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), +// funding_destination_account: ISSUER_1, +// policy_ipfs_cid: Some(ipfs_hash()), +// }; +// +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// vec![], +// ); +// +// inst.mint_plmc_to(vec![ +// (BUYER_4, 500_000 * PLMC).into(), +// (BUYER_5, 500_000 * PLMC).into(), +// (BUYER_6, 500_000 * PLMC).into(), +// (BUYER_7, 500_000 * PLMC).into(), +// (BUYER_8, 500_000 * PLMC).into(), +// (BUYER_9, 500_000 * PLMC).into(), +// ]); +// +// inst.mint_foreign_asset_to(vec![ +// (BUYER_4, 500_000 * USDT_UNIT).into(), +// (BUYER_5, 500_000 * USDT_UNIT).into(), +// (BUYER_6, 500_000 * USDT_UNIT).into(), +// (BUYER_7, 500_000 * USDT_UNIT).into(), +// (BUYER_8, 500_000 * USDT_UNIT).into(), +// (BUYER_9, 500_000 * USDT_UNIT).into(), +// ]); +// +// // total contributions with same DID above 30k CT (300k USD) should fail for retail +// inst.execute(|| { +// assert_ok!(Pallet::::do_contribute( +// &BUYER_4, +// project_id, +// 28_000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(BUYER_4), +// InvestorType::Retail, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::do_contribute( +// &BUYER_5, +// project_id, +// 2001 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// // note we use the same did as bidder 1, on a different account +// generate_did_from_account(BUYER_4), +// InvestorType::Retail, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// Error::::TooHigh +// ); +// }); +// // bidding 2k total works +// inst.execute(|| { +// assert_ok!(Pallet::::do_contribute( +// &BUYER_5, +// project_id, +// 2000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// // note we use the same did as bidder 1, on a different account +// generate_did_from_account(BUYER_4), +// InvestorType::Retail, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )); +// }); +// +// // total contributions with same DID above 2k CT (20k USD) should fail for professionals +// inst.execute(|| { +// assert_ok!(Pallet::::do_contribute( +// &BUYER_6, +// project_id, +// 1800 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(BUYER_6), +// InvestorType::Professional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::do_contribute( +// &BUYER_7, +// project_id, +// 201 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// // note we use the same did as bidder 1, on a different account +// generate_did_from_account(BUYER_6), +// InvestorType::Professional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// Error::::TooHigh +// ); +// }); +// // bidding 2k total works +// inst.execute(|| { +// assert_ok!(Pallet::::do_contribute( +// &BUYER_7, +// project_id, +// 200 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// // note we use the same did as bidder 1, on a different account +// generate_did_from_account(BUYER_6), +// InvestorType::Professional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )); +// }); +// +// // total contributions with same DID above 5k CT (50 USD) should fail for institutionals +// inst.execute(|| { +// assert_ok!(Pallet::::do_contribute( +// &BUYER_8, +// project_id, +// 4690 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// generate_did_from_account(BUYER_8), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )); +// }); +// inst.execute(|| { +// assert_noop!( +// Pallet::::do_contribute( +// &BUYER_9, +// project_id, +// 311 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// // note we use the same did as bidder 3, on a different account +// generate_did_from_account(BUYER_8), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// Error::::TooHigh +// ); +// }); +// // bidding 5k total works +// inst.execute(|| { +// assert_ok!(Pallet::::do_contribute( +// &BUYER_9, +// project_id, +// 310 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// // note we use the same did as bidder 3, on a different account +// generate_did_from_account(BUYER_8), +// InvestorType::Institutional, +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// )); +// }); +// } +// +// #[test] +// fn insufficient_funds() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// vec![], +// ); +// +// let jwt = get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ); +// let contribution = ContributionParams::new(BUYER_1, 1_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// // 1 unit less native asset than needed +// let plmc_funding = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_funding.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// inst.execute(|| Balances::burn_from(&BUYER_1, 1, Precision::BestEffort, Fortitude::Force)).unwrap(); +// +// let foreign_funding = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// inst.mint_foreign_asset_to(foreign_funding.clone()); +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_1), +// jwt.clone(), +// project_id, +// 1_000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// +// // 1 unit less funding asset than needed +// let plmc_funding = inst.calculate_contributed_plmc_spent(vec![contribution.clone()], wap, false); +// let plmc_existential_deposits = plmc_funding.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_funding.clone()); +// inst.mint_plmc_to(plmc_existential_deposits.clone()); +// let foreign_funding = inst.calculate_contributed_funding_asset_spent(vec![contribution.clone()], wap); +// +// inst.execute(|| ForeignAssets::set_balance(AcceptedFundingAsset::USDT.to_assethub_id(), &BUYER_1, 0)); +// inst.mint_foreign_asset_to(foreign_funding.clone()); +// +// inst.execute(|| { +// ForeignAssets::burn_from( +// AcceptedFundingAsset::USDT.to_assethub_id(), +// &BUYER_1, +// 100, +// Precision::BestEffort, +// Fortitude::Force, +// ) +// }) +// .unwrap(); +// +// inst.execute(|| { +// assert_noop!( +// Pallet::::contribute( +// RuntimeOrigin::signed(BUYER_1), +// jwt, +// project_id, +// 1_000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT, +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// } +// +// #[test] +// fn called_outside_remainder_round() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_4, +// None, +// default_evaluations(), +// default_bids(), +// ); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_1), +// get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// project_metadata.clone().policy_ipfs_cid.unwrap() +// ), +// project_id, +// 1000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT +// ), +// Error::::IncorrectRound +// ); +// }); +// } +// +// #[test] +// fn contribute_with_unaccepted_currencies() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let mut project_metadata_usdt = default_project_metadata(ISSUER_2); +// project_metadata_usdt.participation_currencies = vec![AcceptedFundingAsset::USDT].try_into().unwrap(); +// +// let mut project_metadata_usdc = default_project_metadata(ISSUER_3); +// project_metadata_usdc.participation_currencies = vec![AcceptedFundingAsset::USDC].try_into().unwrap(); +// +// let evaluations = default_evaluations(); +// +// let usdt_bids = default_bids() +// .into_iter() +// .map(|mut b| { +// b.asset = AcceptedFundingAsset::USDT; +// b +// }) +// .collect::>(); +// +// let usdc_bids = default_bids() +// .into_iter() +// .map(|mut b| { +// b.asset = AcceptedFundingAsset::USDC; +// b +// }) +// .collect::>(); +// +// let usdt_contribution = ContributionParams::new(BUYER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// let usdc_contribution = ContributionParams::new(BUYER_2, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDC); +// let dot_contribution = ContributionParams::new(BUYER_3, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::DOT); +// +// let project_id_usdc = inst.create_remainder_contributing_project( +// project_metadata_usdc, +// ISSUER_3, +// None, +// evaluations.clone(), +// usdc_bids, +// vec![], +// ); +// assert_err!( +// inst.contribute_for_users(project_id_usdc, vec![usdt_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// +// let project_id_usdt = inst.create_remainder_contributing_project( +// project_metadata_usdt, +// ISSUER_2, +// None, +// evaluations.clone(), +// usdt_bids, +// vec![], +// ); +// assert_err!( +// inst.contribute_for_users(project_id_usdt, vec![usdc_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// assert_err!( +// inst.contribute_for_users(project_id_usdt, vec![dot_contribution.clone()]), +// Error::::FundingAssetNotAccepted +// ); +// } +// +// #[test] +// fn cannot_use_evaluation_bond_on_another_project_contribution() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata_1 = default_project_metadata(ISSUER_1); +// let project_metadata_2 = default_project_metadata(ISSUER_2); +// +// let mut evaluations_1 = default_evaluations(); +// let evaluations_2 = default_evaluations(); +// +// let evaluator_contributor = 69; +// let evaluation_amount = 420 * USD_UNIT; +// let evaluator_contribution = +// ContributionParams::new(evaluator_contributor, 600 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); +// evaluations_1.push((evaluator_contributor, evaluation_amount).into()); +// +// let _project_id_1 = inst.create_remainder_contributing_project( +// project_metadata_1.clone(), +// ISSUER_1, +// None, +// evaluations_1, +// default_bids(), +// vec![], +// ); +// let project_id_2 = inst.create_remainder_contributing_project( +// project_metadata_2.clone(), +// ISSUER_2, +// None, +// evaluations_2, +// default_bids(), +// vec![], +// ); +// +// let wap = inst.get_project_details(project_id_2).weighted_average_price.unwrap(); +// +// // Necessary Mints +// let already_bonded_plmc = inst +// .calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount).into()], false)[0] +// .plmc_amount; +// let usable_evaluation_plmc = +// already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; +// let necessary_plmc_for_contribution = +// inst.calculate_contributed_plmc_spent(vec![evaluator_contribution.clone()], wap, false)[0].plmc_amount; +// let necessary_usdt_for_contribution = +// inst.calculate_contributed_funding_asset_spent(vec![evaluator_contribution.clone()], wap); +// inst.mint_plmc_to(vec![UserToPLMCBalance::new( +// evaluator_contributor, +// necessary_plmc_for_contribution - usable_evaluation_plmc, +// )]); +// inst.mint_foreign_asset_to(necessary_usdt_for_contribution); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::contribute( +// RuntimeOrigin::signed(evaluator_contributor), +// get_mock_jwt_with_cid( +// evaluator_contributor, +// InvestorType::Retail, +// generate_did_from_account(evaluator_contributor), +// project_metadata_2.clone().policy_ipfs_cid.unwrap(), +// ), +// project_id_2, +// evaluator_contribution.amount, +// evaluator_contribution.multiplier, +// evaluator_contribution.asset +// ), +// Error::::ParticipantNotEnoughFunds +// ); +// }); +// } +// +// #[test] +// fn wrong_policy_on_jwt() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// vec![], +// ); +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_1), +// get_mock_jwt_with_cid( +// BUYER_1, +// InvestorType::Retail, +// generate_did_from_account(BUYER_1), +// "wrong_cid".as_bytes().to_vec().try_into().unwrap() +// ), +// project_id, +// 5000 * CT_UNIT, +// 1u8.try_into().unwrap(), +// AcceptedFundingAsset::USDT +// ), +// Error::::PolicyMismatch +// ); +// }); +// } +// } +// } diff --git a/pallets/funding/src/tests/6_funding_end.rs b/pallets/funding/src/tests/6_funding_end.rs index 1754dd2e6..cf736ac34 100644 --- a/pallets/funding/src/tests/6_funding_end.rs +++ b/pallets/funding/src/tests/6_funding_end.rs @@ -1,113 +1,113 @@ -use super::*; -#[cfg(test)] -mod round_flow { - use super::*; - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn evaluator_slash_is_decided() { - let (mut inst, project_id) = create_project_with_funding_percentage(20, None, true); - assert_eq!( - inst.get_project_details(project_id).status, - ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) - ); - assert_eq!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, - EvaluatorsOutcome::Slashed - ); - } - - #[test] - fn evaluator_unchanged_is_decided() { - let (mut inst, project_id) = - create_project_with_funding_percentage(80, Some(FundingOutcomeDecision::AcceptFunding), true); - assert_eq!( - inst.get_project_details(project_id).status, - ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful) - ); - assert_eq!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, - EvaluatorsOutcome::Unchanged - ); - } - - #[test] - fn evaluator_reward_is_decided() { - let (mut inst, project_id) = create_project_with_funding_percentage(95, None, true); - let project_details = inst.get_project_details(project_id); - let project_metadata = inst.get_project_metadata(project_id); - assert_eq!( - inst.get_project_details(project_id).status, - ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful) - ); - - // We want to test rewards over the 3 brackets, which means > 5MM USD funded - const USD_REACHED: u128 = 9_500_000 * USD_UNIT; - const FEE_1: Percent = Percent::from_percent(10u8); - const FEE_2: Percent = Percent::from_percent(8); - const FEE_3: Percent = Percent::from_percent(6); - - let fee_1 = FEE_1 * 1_000_000 * USD_UNIT; - let fee_2 = FEE_2 * 4_000_000 * USD_UNIT; - let fee_3 = FEE_3 * 4_500_000 * USD_UNIT; - - let total_fee = Perquintill::from_rational(fee_1 + fee_2 + fee_3, USD_REACHED); - - let total_ct_fee = - total_fee * (project_metadata.total_allocation_size - project_details.remaining_contribution_tokens); - - let total_evaluator_reward = - Perquintill::from_percent(95u64) * Perquintill::from_percent(30) * total_ct_fee; - - let early_evaluator_reward = Perquintill::from_percent(20u64) * total_evaluator_reward; - - let normal_evaluator_reward = Perquintill::from_percent(80u64) * total_evaluator_reward; - const EARLY_EVALUATOR_TOTAL_USD_BONDED: u128 = 1_000_000 * USD_UNIT; - const NORMAL_EVALUATOR_TOTAL_USD_BONDED: u128 = 1_070_000 * USD_UNIT; - - let expected_reward_info = RewardInfoOf:: { - early_evaluator_reward_pot: early_evaluator_reward, - normal_evaluator_reward_pot: normal_evaluator_reward, - early_evaluator_total_bonded_usd: EARLY_EVALUATOR_TOTAL_USD_BONDED, - normal_evaluator_total_bonded_usd: NORMAL_EVALUATOR_TOTAL_USD_BONDED, - }; - assert_eq!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, - EvaluatorsOutcome::Rewarded(expected_reward_info) - ); - } - - #[test] - fn auction_oversubscription() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let auction_allocation = - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; - let bucket_size = Percent::from_percent(10) * auction_allocation; - let bids = vec![ - (BIDDER_1, auction_allocation).into(), - (BIDDER_2, bucket_size).into(), - (BIDDER_3, bucket_size).into(), - (BIDDER_4, bucket_size).into(), - (BIDDER_5, bucket_size).into(), - (BIDDER_6, bucket_size).into(), - ]; - - let project_id = inst.create_finished_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - bids, - default_community_buys(), - default_remainder_buys(), - ); - - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - dbg!(wap); - assert!(wap > project_metadata.minimum_price); - } - } -} +// use super::*; +// #[cfg(test)] +// mod round_flow { +// use super::*; +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn evaluator_slash_is_decided() { +// let (mut inst, project_id) = create_project_with_funding_percentage(20, None, true); +// assert_eq!( +// inst.get_project_details(project_id).status, +// ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) +// ); +// assert_eq!( +// inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, +// EvaluatorsOutcome::Slashed +// ); +// } +// +// #[test] +// fn evaluator_unchanged_is_decided() { +// let (mut inst, project_id) = +// create_project_with_funding_percentage(80, Some(FundingOutcomeDecision::AcceptFunding), true); +// assert_eq!( +// inst.get_project_details(project_id).status, +// ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful) +// ); +// assert_eq!( +// inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, +// EvaluatorsOutcome::Unchanged +// ); +// } +// +// #[test] +// fn evaluator_reward_is_decided() { +// let (mut inst, project_id) = create_project_with_funding_percentage(95, None, true); +// let project_details = inst.get_project_details(project_id); +// let project_metadata = inst.get_project_metadata(project_id); +// assert_eq!( +// inst.get_project_details(project_id).status, +// ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful) +// ); +// +// // We want to test rewards over the 3 brackets, which means > 5MM USD funded +// const USD_REACHED: u128 = 9_500_000 * USD_UNIT; +// const FEE_1: Percent = Percent::from_percent(10u8); +// const FEE_2: Percent = Percent::from_percent(8); +// const FEE_3: Percent = Percent::from_percent(6); +// +// let fee_1 = FEE_1 * 1_000_000 * USD_UNIT; +// let fee_2 = FEE_2 * 4_000_000 * USD_UNIT; +// let fee_3 = FEE_3 * 4_500_000 * USD_UNIT; +// +// let total_fee = Perquintill::from_rational(fee_1 + fee_2 + fee_3, USD_REACHED); +// +// let total_ct_fee = +// total_fee * (project_metadata.total_allocation_size - project_details.remaining_contribution_tokens); +// +// let total_evaluator_reward = +// Perquintill::from_percent(95u64) * Perquintill::from_percent(30) * total_ct_fee; +// +// let early_evaluator_reward = Perquintill::from_percent(20u64) * total_evaluator_reward; +// +// let normal_evaluator_reward = Perquintill::from_percent(80u64) * total_evaluator_reward; +// const EARLY_EVALUATOR_TOTAL_USD_BONDED: u128 = 1_000_000 * USD_UNIT; +// const NORMAL_EVALUATOR_TOTAL_USD_BONDED: u128 = 1_070_000 * USD_UNIT; +// +// let expected_reward_info = RewardInfoOf:: { +// early_evaluator_reward_pot: early_evaluator_reward, +// normal_evaluator_reward_pot: normal_evaluator_reward, +// early_evaluator_total_bonded_usd: EARLY_EVALUATOR_TOTAL_USD_BONDED, +// normal_evaluator_total_bonded_usd: NORMAL_EVALUATOR_TOTAL_USD_BONDED, +// }; +// assert_eq!( +// inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, +// EvaluatorsOutcome::Rewarded(expected_reward_info) +// ); +// } +// +// #[test] +// fn auction_oversubscription() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let auction_allocation = +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; +// let bucket_size = Percent::from_percent(10) * auction_allocation; +// let bids = vec![ +// (BIDDER_1, auction_allocation).into(), +// (BIDDER_2, bucket_size).into(), +// (BIDDER_3, bucket_size).into(), +// (BIDDER_4, bucket_size).into(), +// (BIDDER_5, bucket_size).into(), +// (BIDDER_6, bucket_size).into(), +// ]; +// +// let project_id = inst.create_finished_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// bids, +// default_community_buys(), +// default_remainder_buys(), +// ); +// +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// dbg!(wap); +// assert!(wap > project_metadata.minimum_price); +// } +// } +// } diff --git a/pallets/funding/src/tests/7_settlement.rs b/pallets/funding/src/tests/7_settlement.rs index f35835ea5..28b8ee977 100644 --- a/pallets/funding/src/tests/7_settlement.rs +++ b/pallets/funding/src/tests/7_settlement.rs @@ -1,1539 +1,1539 @@ -use super::*; - -#[cfg(test)] -mod round_flow { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn can_fully_settle_accepted_project() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - 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_total_funding_paid_out(project_id, bids.clone(), contributions.clone()); - 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] - fn can_fully_settle_failed_project() { - let percentage = 33u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - 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_migrations_created(project_id, evaluations, percentage); - inst.assert_bids_migrations_created(project_id, bids, false); - inst.assert_contributions_migrations_created(project_id, contributions, false); - } - } -} - -#[cfg(test)] -mod settle_successful_evaluation_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn evaluation_unchanged() { - let percentage = 89u64; - - let (mut inst, project_id) = - create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding), true); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - let prev_balance = inst.get_free_plmc_balance_for(evaluator); - - assert_eq!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, - EvaluatorsOutcomeOf::::Unchanged - ); - - assert_ok!(inst.execute(|| PolimecFunding::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ))); - - let post_balance = inst.get_free_plmc_balance_for(evaluator); - assert_eq!(post_balance, prev_balance + first_evaluation.current_plmc_bond); - } - - #[test] - fn evaluation_slashed() { - let percentage = 50u64; - let (mut inst, project_id) = - create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding), true); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - let prev_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; - - assert_eq!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, - EvaluatorsOutcomeOf::::Slashed - ); - - assert_ok!(inst.execute(|| PolimecFunding::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ))); - - let post_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; - assert_eq!( - post_balance, - prev_balance + - (Percent::from_percent(100) - ::EvaluatorSlash::get()) * - first_evaluation.current_plmc_bond - ); - } - - #[test] - fn evaluation_rewarded() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_finished_project( - project_metadata.clone(), - ISSUER_1, - None, - vec![ - UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_3, 320_000 * USD_UNIT), - ], - inst.generate_bids_from_total_ct_percent( - project_metadata.clone(), - 50, - default_weights(), - default_bidders(), - default_multipliers(), - ), - inst.generate_contributions_from_total_ct_percent( - project_metadata.clone(), - 50, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ), - vec![], - ); - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - // The rewards are calculated as follows: - // Data: - // - Funding USD reached: 10_000_000 USD - // - Total CTs sold: 1_000_000 CT - // - USD target reached percent: 100% - - // Step 1) Calculate the total USD fee: - // USD fee 1 = 0.1 * 1_000_000 = 100_000 USD - // USD fee 2 = 0.08 * 4_000_000 = 320_000 USD - // USD fee 3 = 0.06 * 5_000_000 = 300_000 USD - // Total USD fee = 100_000 + 320_000 + 300_000 = 720_000 USD - - // Step 2) Calculate CT fee as follows: - // Percent fee = Total USD fee / Funding USD reached = 720_000 / 10_000_000 = 0.072 - // CT fee = Percent fee * Total CTs sold = 0.072 * 1_000_000 = 72_000 CT - - // Step 3) Calculate Early and Normal evaluator reward pots: - // Total evaluators reward pot = CT fee * 0.3 * USD target reached percent = 72_000 * 0.3 * 1 = 21_600 CT - // Early evaluators reward pot = Total evaluators reward pot * 0.2 = 21_600 * 0.2 = 4_320 CT - // Normal evaluators reward pot = Total evaluators reward pot * 0.8 = 21_600 * 0.8 = 17_280 CT - - // Step 4) Calculate the early and normal weights of each evaluation: - // Evaluation 1 = 500_000 USD - // Evaluation 2 = 250_000 USD - // Evaluation 3 = 320_000 USD - - // Early amount 1 = 500_000 USD - // Early amount 2 = 250_000 USD - // Early amount 3 = 250_000 USD - - // Total Normal amount = Evaluation 1 + Evaluation 2 + Evaluation 3 = 500_000 + 250_000 + 320_000 = 1_070_000 USD - // Total Early amount = 10% of USD target = 1_000_000 USD - - // Early weight 1 = Early amount 1 / Total Early amount = 500_000 / 1_000_000 = 0.5 - // Early weight 2 = Early amount 2 / Total Early amount = 250_000 / 1_000_000 = 0.25 - // Early weight 3 = Early amount 3 / Total Early amount = 250_000 / 1_000_000 = 0.25 - - // Normal weight 1 = Evaluation 1 / Total Normal amount = 500_000 / 1_070_000 = 0.467289719626168 - // Normal weight 2 = Evaluation 2 / Total Normal amount = 250_000 / 1_070_000 = 0.233644859813084 - // Normal weight 3 = Evaluation 3 / Total Normal amount = 320_000 / 1_070_000 = 0.299065420560748 - - // Step 5) Calculate the rewards for each evaluation: - // Evaluation 1 Early reward = Early weight 1 * Early evaluators reward pot = 0.5 * 4_320 = 2_160 CT - // Evaluation 2 Early reward = Early weight 2 * Early evaluators reward pot = 0.25 * 4_320 = 1_080 CT - // Evaluation 3 Early reward = Early weight 3 * Early evaluators reward pot = 0.25 * 4_320 = 1_080 CT - - // Evaluation 1 Normal reward = Normal weight 1 * Normal evaluators reward pot = 0.467289719626168 * 17_280 = 8'074.766355140186916 CT - // Evaluation 2 Normal reward = Normal weight 2 * Normal evaluators reward pot = 0.233644859813084 * 17_280 = 4'037.383177570093458 CT - // Evaluation 3 Normal reward = Normal weight 3 * Normal evaluators reward pot = 0.299065420560748 * 17_280 = 5'167.850467289719626 CT - - // Evaluation 1 Total reward = Evaluation 1 Early reward + Evaluation 1 Normal reward = 2_160 + 8_066 = 10'234.766355140186916 CT - // Evaluation 2 Total reward = Evaluation 2 Early reward + Evaluation 2 Normal reward = 1_080 + 4_033 = 5'117.383177570093458 CT - // Evaluation 3 Total reward = Evaluation 3 Early reward + Evaluation 3 Normal reward = 1_080 + 5_201 = 6'247.850467289719626 CT - - const EVAL_1_REWARD: u128 = 10_234_766355140186916; - const EVAL_2_REWARD: u128 = 5_117_383177570093458; - const EVAL_3_REWARD: u128 = 6_247_850467289719626; - - let prev_ct_balances = inst.get_ct_asset_balances_for(project_id, vec![ISSUER_1, ISSUER_2, ISSUER_3]); - assert!(prev_ct_balances.iter().all(|x| *x == Zero::zero())); - - let evals = vec![(EVALUATOR_1, EVAL_1_REWARD), (EVALUATOR_2, EVAL_2_REWARD), (EVALUATOR_3, EVAL_3_REWARD)]; - - for (evaluator, expected_reward) in evals { - let evaluation_locked_plmc = - inst.get_reserved_plmc_balance_for(evaluator, HoldReason::Evaluation(project_id).into()); - let free_plmc = inst.get_free_plmc_balance_for(evaluator); - assert_ok!(inst.execute(|| PolimecFunding::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - evaluator - 21 // The First evaluation index is 0, the first evaluator account is 21 - ))); - let ct_rewarded = inst.get_ct_asset_balance_for(project_id, evaluator); - assert_close_enough!(ct_rewarded, expected_reward, Perquintill::from_float(0.9999)); - assert_eq!(inst.get_reserved_plmc_balance_for(evaluator, HoldReason::Evaluation(project_id).into()), 0); - assert_eq!(inst.get_free_plmc_balance_for(evaluator), free_plmc + evaluation_locked_plmc); - inst.assert_migration(project_id, evaluator, expected_reward, 0, ParticipationType::Evaluation, true); - } - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn cannot_settle_twice() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - inst.execute(|| { - let evaluator = first_evaluation.evaluator; - assert_ok!(crate::Pallet::::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - )); - assert_noop!( - crate::Pallet::::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ), - Error::::ParticipationNotFound - ); - }); - } - - #[test] - fn cannot_be_called_on_wrong_outcome() { - let percentage = 10u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - - inst.execute(|| { - assert_noop!( - PolimecFunding::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ), - Error::::FundingSuccessSettlementNotStarted - ); - }); - } - - #[test] - fn cannot_be_called_before_settlement_started() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - - inst.execute(|| { - assert_noop!( - PolimecFunding::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ), - Error::::FundingSuccessSettlementNotStarted - ); - }); - } - } -} - -#[cfg(test)] -mod settle_successful_bid_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn bid_is_correctly_settled() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let issuer = ISSUER_1; - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); - let bid_1 = BidParams::new(BIDDER_1, 1000 * CT_UNIT, 1, AcceptedFundingAsset::USDT); - let bid_2 = BidParams::new(BIDDER_2, 1000 * CT_UNIT, 2, AcceptedFundingAsset::USDT); - - let community_contributions = inst.generate_contributions_from_total_ct_percent( - project_metadata.clone(), - 90, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ); - - let project_id = inst.create_finished_project( - project_metadata.clone(), - issuer, - None, - evaluations, - vec![bid_1, bid_2], - community_contributions, - vec![], - ); - - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - // First bid assertions - let stored_bid = inst.execute(|| Bids::::get((project_id, BIDDER_1, 0)).unwrap()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_1); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_1, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_1); - let issuer_usdt_balance = - inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_1, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_bid.plmc_bond); - assert_eq!(ct_amount, 0u128); - assert_eq!(issuer_usdt_balance, 0u128); - assert!(unvested_amount.is_none()); - - inst.execute(|| { - assert_ok!(PolimecFunding::settle_successful_bid( - RuntimeOrigin::signed(BIDDER_1), - project_id, - BIDDER_1, - 0 - )); - }); - - assert!(inst.execute(|| Contributions::::get((project_id, BIDDER_1, 0)).is_none())); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_1); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_1, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_1); - let issuer_usdt_balance = - inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_1, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed() + stored_bid.plmc_bond); - assert_eq!(plmc_held_amount, 0u128); - assert_eq!(ct_amount, stored_bid.final_ct_amount); - assert_eq!( - issuer_usdt_balance, - stored_bid.final_ct_usd_price.saturating_mul_int(stored_bid.final_ct_amount) - ); - assert!(unvested_amount.is_none()); - inst.assert_migration(project_id, BIDDER_1, stored_bid.final_ct_amount, 0, ParticipationType::Bid, true); - - // Second bid assertions - let stored_bid = inst.execute(|| Bids::::get((project_id, BIDDER_2, 1)).unwrap()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_2); - let issuer_usdt_balance_2 = - inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_2, - HoldReason::Participation(project_id).into(), - ) - }); - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_bid.plmc_bond); - assert_eq!(ct_amount, 0u128); - assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance); - assert!(unvested_amount.is_none()); - - inst.execute(|| { - assert_ok!(PolimecFunding::settle_successful_bid( - RuntimeOrigin::signed(BIDDER_2), - project_id, - BIDDER_2, - 1 - )); - }); - - assert!(inst.execute(|| Contributions::::get((project_id, BIDDER_2, 1)).is_none())); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_2); - let issuer_usdt_balance_2 = - inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst - .execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_2, - HoldReason::Participation(project_id).into(), - ) - }) - .unwrap(); - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_bid.plmc_bond); - assert_eq!(ct_amount, stored_bid.final_ct_amount); - assert_eq!( - issuer_usdt_balance_2, - issuer_usdt_balance + stored_bid.final_ct_usd_price.saturating_mul_int(stored_bid.final_ct_amount) - ); - assert_eq!(unvested_amount, stored_bid.plmc_bond); - - let vesting_time = stored_bid.multiplier.calculate_vesting_duration::(); - let now = inst.current_block(); - inst.jump_to_block(vesting_time + now + 1u64); - inst.execute(|| { - assert_ok!(::Vesting::vest( - RuntimeOrigin::signed(BIDDER_2), - HoldReason::Participation(project_id).into() - )); - }); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_2, - HoldReason::Participation(project_id).into(), - ) - }); - assert!(unvested_amount.is_none()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); - assert_eq!(plmc_free_amount, inst.get_ed() + stored_bid.plmc_bond); - assert_eq!(plmc_held_amount, 0u128); - inst.assert_migration(project_id, BIDDER_2, stored_bid.final_ct_amount, 1, ParticipationType::Bid, true); - } - - #[test] - fn rejected_bids_dont_get_vest_schedule() { - // * Test Setup * - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - // Project variables - let issuer = ISSUER_1; - let project_metadata = default_project_metadata(issuer); - let evaluations = default_evaluations(); - let auction_token_allocation = - project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; - let mut bids = inst.generate_bids_from_total_usd( - Percent::from_percent(80) * project_metadata.minimum_price.saturating_mul_int(auction_token_allocation), - project_metadata.minimum_price, - vec![60, 40], - vec![BIDDER_1, BIDDER_2], - vec![1u8, 1u8], - ); - let community_contributions = default_community_buys(); - - // Add rejected and accepted bids to test our vesting schedule assertions - let available_tokens = - auction_token_allocation.saturating_sub(bids.iter().fold(0, |acc, bid| acc + bid.amount)); - - let rejected_bid = vec![BidParams::new(BIDDER_5, available_tokens, 1u8, AcceptedFundingAsset::USDT)]; - let accepted_bid = vec![BidParams::new(BIDDER_4, available_tokens, 2u8, AcceptedFundingAsset::USDT)]; - bids.extend(rejected_bid.clone()); - bids.extend(accepted_bid.clone()); - - let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); - - // Mint the necessary bidding balances - let bidders_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - true, - ); - let bidders_existential_deposits = bidders_plmc.accounts().existential_deposits(); - inst.mint_plmc_to(bidders_plmc.clone()); - inst.mint_plmc_to(bidders_existential_deposits); - let bidders_funding_assets = inst - .calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - ); - inst.mint_foreign_asset_to(bidders_funding_assets); - - inst.bid_for_users(project_id, bids).unwrap(); - - inst.start_community_funding(project_id).unwrap(); - - // Mint the necessary community contribution balances - let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); - let contributors_plmc = - inst.calculate_contributed_plmc_spent(community_contributions.clone(), final_price, false); - let contributors_existential_deposits = contributors_plmc.accounts().existential_deposits(); - inst.mint_plmc_to(contributors_plmc.clone()); - inst.mint_plmc_to(contributors_existential_deposits); - let contributors_funding_assets = - inst.calculate_contributed_funding_asset_spent(community_contributions.clone(), final_price); - inst.mint_foreign_asset_to(contributors_funding_assets); - - inst.contribute_for_users(project_id, community_contributions).unwrap(); - - // Finish and Settle project - inst.start_remainder_or_end_funding(project_id).unwrap(); - inst.finish_funding(project_id, None).unwrap(); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.settle_project(project_id).unwrap(); - - let plmc_locked_for_accepted_bid = - inst.calculate_auction_plmc_charged_with_given_price(&accepted_bid, final_price, false); - let plmc_locked_for_rejected_bid = - inst.calculate_auction_plmc_charged_with_given_price(&rejected_bid, final_price, false); - - let UserToPLMCBalance { account: accepted_user, plmc_amount: accepted_plmc_amount } = - plmc_locked_for_accepted_bid[0]; - let UserToPLMCBalance { account: rejected_user, .. } = plmc_locked_for_rejected_bid[0]; - - // * Assertions * - let schedule = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &accepted_user, - HoldReason::Participation(project_id).into(), - ) - }); - assert_close_enough!(schedule.unwrap(), accepted_plmc_amount, Perquintill::from_float(0.99)); - assert!(inst - .execute(|| { - ::Vesting::total_scheduled_amount( - &rejected_user, - HoldReason::Participation(project_id).into(), - ) - }) - .is_none()); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn cannot_settle_twice() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); - inst.execute(|| { - let bidder = first_bid.bidder; - assert_ok!(crate::Pallet::::settle_successful_bid( - RuntimeOrigin::signed(bidder), - project_id, - bidder, - first_bid.id - )); - assert_noop!( - crate::Pallet::::settle_successful_bid( - RuntimeOrigin::signed(bidder), - project_id, - bidder, - first_bid.id - ), - Error::::ParticipationNotFound - ); - }); - } - - #[test] - fn cannot_be_called_on_wrong_outcome() { - let percentage = 10u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); - let bidder = first_bid.bidder; - inst.execute(|| { - assert_noop!( - crate::Pallet::::settle_successful_bid( - RuntimeOrigin::signed(bidder), - project_id, - bidder, - first_bid.id - ), - Error::::FundingSuccessSettlementNotStarted - ); - }); - } - - #[test] - fn cannot_be_called_before_settlement_started() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); - - let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); - let bidder = first_bid.bidder; - inst.execute(|| { - assert_noop!( - crate::Pallet::::settle_successful_bid( - RuntimeOrigin::signed(bidder), - project_id, - bidder, - first_bid.id - ), - Error::::FundingSuccessSettlementNotStarted - ); - }); - } - } -} - -#[cfg(test)] -mod settle_successful_contribution_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn contribution_is_correctly_settled() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let issuer = ISSUER_1; - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); - let bids = inst.generate_bids_from_total_ct_percent( - project_metadata.clone(), - 50, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let mut community_contributions = inst.generate_contributions_from_total_ct_percent( - project_metadata.clone(), - 40, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ); - - let contribution_mul_1 = - ContributionParams::::new(BUYER_6, 1000 * CT_UNIT, 1, AcceptedFundingAsset::USDT); - let contribution_mul_2 = - ContributionParams::::new(BUYER_7, 1000 * CT_UNIT, 2, AcceptedFundingAsset::USDT); - - community_contributions.push(contribution_mul_1); - - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - issuer, - None, - evaluations, - bids, - community_contributions, - ); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution_mul_2.clone()], wap, false); - let plmc_ed = plmc_required.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_required.clone()); - inst.mint_plmc_to(plmc_ed); - - let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution_mul_2.clone()], wap); - inst.mint_foreign_asset_to(usdt_required.clone()); - - inst.execute(|| { - assert_ok!(PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_7), - get_mock_jwt_with_cid( - BUYER_7, - InvestorType::Professional, - generate_did_from_account(BUYER_7), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - project_id, - contribution_mul_2.amount, - contribution_mul_2.multiplier, - contribution_mul_2.asset - )); - }); - - inst.finish_funding(project_id, None).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - // First contribution assertions - let stored_contribution = - inst.execute(|| Contributions::::get((project_id, BUYER_6, 5)).unwrap()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_6); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_6, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_6); - let issuer_usdt_balance = - inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_6, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); - assert_eq!(ct_amount, 0u128); - assert_eq!(issuer_usdt_balance, 0u128); - assert!(unvested_amount.is_none()); - - inst.execute(|| { - assert_ok!(PolimecFunding::settle_successful_contribution( - RuntimeOrigin::signed(BUYER_6), - project_id, - BUYER_6, - 5 - )); - }); - - assert!(inst.execute(|| Contributions::::get((project_id, BUYER_6, 6)).is_none())); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_6); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_6, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_6); - let issuer_usdt_balance = - inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_6, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed() + stored_contribution.plmc_bond); - assert_eq!(plmc_held_amount, 0u128); - assert_eq!(ct_amount, stored_contribution.ct_amount); - assert_eq!(issuer_usdt_balance, stored_contribution.usd_contribution_amount); - assert!(unvested_amount.is_none()); - inst.assert_migration( - project_id, - BUYER_6, - stored_contribution.ct_amount, - 5, - ParticipationType::Contribution, - true, - ); - - // Second contribution assertions - let stored_contribution = - inst.execute(|| Contributions::::get((project_id, BUYER_7, 6)).unwrap()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_7); - let issuer_usdt_balance_2 = - inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_7, - HoldReason::Participation(project_id).into(), - ) - }); - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); - assert_eq!(ct_amount, 0u128); - assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance); - assert!(unvested_amount.is_none()); - - inst.execute(|| { - assert_ok!(PolimecFunding::settle_successful_contribution( - RuntimeOrigin::signed(BUYER_7), - project_id, - BUYER_7, - 6 - )); - }); - - assert!(inst.execute(|| Contributions::::get((project_id, BUYER_7, 7)).is_none())); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_7); - let issuer_usdt_balance_2 = - inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst - .execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_7, - HoldReason::Participation(project_id).into(), - ) - }) - .unwrap(); - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); - assert_eq!(ct_amount, stored_contribution.ct_amount); - assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance + stored_contribution.usd_contribution_amount); - assert_eq!(unvested_amount, stored_contribution.plmc_bond); - - let vesting_time = stored_contribution.multiplier.calculate_vesting_duration::(); - let now = inst.current_block(); - inst.jump_to_block(vesting_time + now + 1u64); - inst.execute(|| { - assert_ok!(::Vesting::vest( - RuntimeOrigin::signed(BUYER_7), - HoldReason::Participation(project_id).into() - )); - }); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_7, - HoldReason::Participation(project_id).into(), - ) - }); - assert!(unvested_amount.is_none()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); - assert_eq!(plmc_free_amount, inst.get_ed() + stored_contribution.plmc_bond); - assert_eq!(plmc_held_amount, 0u128); - inst.assert_migration( - project_id, - BUYER_7, - stored_contribution.ct_amount, - 6, - ParticipationType::Contribution, - true, - ); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn cannot_settle_twice() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); - inst.execute(|| { - let contributor = first_contribution.contributor; - assert_ok!(crate::Pallet::::settle_successful_contribution( - RuntimeOrigin::signed(contributor), - project_id, - contributor, - first_contribution.id - )); - assert_noop!( - crate::Pallet::::settle_successful_contribution( - RuntimeOrigin::signed(contributor), - project_id, - contributor, - first_contribution.id - ), - Error::::ParticipationNotFound - ); - }); - } - - #[test] - fn cannot_be_called_on_wrong_outcome() { - let percentage = 10u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); - let contributor = first_contribution.contributor; - inst.execute(|| { - assert_noop!( - crate::Pallet::::settle_successful_contribution( - RuntimeOrigin::signed(contributor), - project_id, - contributor, - first_contribution.id - ), - Error::::FundingSuccessSettlementNotStarted - ); - }); - } - - #[test] - fn cannot_be_called_before_settlement_started() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); - let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); - let contributor = first_contribution.contributor; - inst.execute(|| { - assert_noop!( - crate::Pallet::::settle_successful_contribution( - RuntimeOrigin::signed(contributor), - project_id, - contributor, - first_contribution.id - ), - Error::::FundingSuccessSettlementNotStarted - ); - }); - } - } -} - -#[cfg(test)] -mod settle_failed_evaluation_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn evaluation_unchanged() { - let percentage = 89u64; - - let (mut inst, project_id) = - create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding), true); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - let prev_balance = inst.get_free_plmc_balance_for(evaluator); - - assert_eq!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, - EvaluatorsOutcomeOf::::Unchanged - ); - - assert_ok!(inst.execute(|| PolimecFunding::settle_failed_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ))); - - let post_balance = inst.get_free_plmc_balance_for(evaluator); - assert_eq!(post_balance, prev_balance + first_evaluation.current_plmc_bond); - } - - #[test] - fn evaluation_slashed() { - let percentage = 50u64; - let (mut inst, project_id) = - create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding), true); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - let prev_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; - - assert_eq!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, - EvaluatorsOutcomeOf::::Slashed - ); - - assert_ok!(inst.execute(|| PolimecFunding::settle_failed_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ))); - - let post_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; - assert_eq!( - post_balance, - prev_balance + - (Percent::from_percent(100) - ::EvaluatorSlash::get()) * - first_evaluation.current_plmc_bond - ); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn cannot_settle_twice() { - let percentage = 33u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - inst.execute(|| { - let evaluator = first_evaluation.evaluator; - assert_ok!(crate::Pallet::::settle_failed_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - )); - assert_noop!( - crate::Pallet::::settle_failed_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ), - Error::::ParticipationNotFound - ); - }); - } - - #[test] - fn cannot_be_called_on_wrong_outcome() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - - inst.execute(|| { - assert_noop!( - PolimecFunding::settle_failed_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ), - Error::::FundingFailedSettlementNotStarted - ); - }); - } - - #[test] - fn cannot_be_called_before_settlement_started() { - let percentage = 10u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); - - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - - inst.execute(|| { - assert_noop!( - PolimecFunding::settle_failed_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - ), - Error::::FundingFailedSettlementNotStarted - ); - }); - } - } -} - -#[cfg(test)] -mod settle_failed_bid_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn bid_is_correctly_settled() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let issuer = ISSUER_1; - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); - let bid_1 = BidParams::new(BIDDER_1, 1000 * CT_UNIT, 1, AcceptedFundingAsset::USDT); - let bid_2 = BidParams::new(BIDDER_2, 1000 * CT_UNIT, 2, AcceptedFundingAsset::USDT); - - let community_contributions = inst.generate_contributions_from_total_ct_percent( - project_metadata.clone(), - 20, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ); - - let project_id = inst.create_finished_project( - project_metadata.clone(), - issuer, - None, - evaluations, - vec![bid_1, bid_2], - community_contributions, - vec![], - ); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - // First bid assertions - let stored_bid = inst.execute(|| Bids::::get((project_id, BIDDER_1, 0)).unwrap()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_1); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_1, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_1); - let issuer_usdt_balance = - inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_1, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_bid.plmc_bond); - assert_eq!(ct_amount, 0u128); - assert_eq!(issuer_usdt_balance, 0u128); - assert!(unvested_amount.is_none()); - - inst.execute(|| { - assert_ok!(PolimecFunding::settle_failed_bid(RuntimeOrigin::signed(BIDDER_1), project_id, BIDDER_1, 0)); - }); - - assert!(inst.execute(|| Contributions::::get((project_id, BIDDER_1, 0)).is_none())); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_1); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_1, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_1); - let issuer_usdt_balance = - inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_1, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed() + stored_bid.plmc_bond); - assert_eq!(plmc_held_amount, 0u128); - assert_eq!(ct_amount, Zero::zero()); - assert_eq!(issuer_usdt_balance, Zero::zero()); - assert!(unvested_amount.is_none()); - inst.assert_migration(project_id, BIDDER_1, stored_bid.final_ct_amount, 0, ParticipationType::Bid, false); - - // Second bid assertions - let stored_bid = inst.execute(|| Bids::::get((project_id, BIDDER_2, 1)).unwrap()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_2); - let issuer_usdt_balance_2 = - inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_2, - HoldReason::Participation(project_id).into(), - ) - }); - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_bid.plmc_bond); - assert_eq!(ct_amount, 0u128); - assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance); - assert!(unvested_amount.is_none()); - - inst.execute(|| { - assert_ok!(PolimecFunding::settle_failed_bid(RuntimeOrigin::signed(BIDDER_2), project_id, BIDDER_2, 1)); - }); - - assert!(inst.execute(|| Contributions::::get((project_id, BIDDER_2, 1)).is_none())); - let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_2); - let issuer_usdt_balance_2 = - inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BIDDER_2, - HoldReason::Participation(project_id).into(), - ) - }); - assert_eq!(plmc_free_amount, inst.get_ed() + stored_bid.plmc_bond); - assert_eq!(plmc_held_amount, Zero::zero()); - assert_eq!(ct_amount, Zero::zero()); - assert_eq!(issuer_usdt_balance_2, Zero::zero()); - assert!(unvested_amount.is_none()); - - inst.assert_migration(project_id, BIDDER_2, stored_bid.final_ct_amount, 1, ParticipationType::Bid, false); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn cannot_settle_twice() { - let percentage = 33u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - 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), - project_id, - bidder, - first_bid.id - )); - assert_noop!( - crate::Pallet::::settle_failed_bid( - RuntimeOrigin::signed(bidder), - project_id, - bidder, - first_bid.id - ), - Error::::ParticipationNotFound - ); - }); - } - - #[test] - fn cannot_be_called_on_wrong_outcome() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); - let bidder = first_bid.bidder; - inst.execute(|| { - assert_noop!( - crate::Pallet::::settle_failed_bid( - RuntimeOrigin::signed(bidder), - project_id, - bidder, - first_bid.id - ), - Error::::FundingFailedSettlementNotStarted - ); - }); - } - - #[test] - fn cannot_be_called_before_settlement_started() { - let percentage = 10u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); - - let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); - let bidder = first_bid.bidder; - inst.execute(|| { - assert_noop!( - crate::Pallet::::settle_failed_bid( - RuntimeOrigin::signed(bidder), - project_id, - bidder, - first_bid.id - ), - Error::::FundingFailedSettlementNotStarted - ); - }); - } - } -} - -#[cfg(test)] -mod settle_failed_contribution_extrinsic { - use super::*; - - #[cfg(test)] - mod success { - use super::*; - - #[test] - fn contribution_is_correctly_settled() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let issuer = ISSUER_1; - let evaluations = - inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); - let bids = inst.generate_bids_from_total_ct_percent( - project_metadata.clone(), - 10, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let mut community_contributions = inst.generate_contributions_from_total_ct_percent( - project_metadata.clone(), - 10, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ); - - let contribution_mul_1 = - ContributionParams::::new(BUYER_6, 1000 * CT_UNIT, 1, AcceptedFundingAsset::USDT); - let contribution_mul_2 = - ContributionParams::::new(BUYER_7, 1000 * CT_UNIT, 2, AcceptedFundingAsset::USDT); - - community_contributions.push(contribution_mul_1); - - let project_id = inst.create_remainder_contributing_project( - project_metadata.clone(), - issuer, - None, - evaluations, - bids, - community_contributions, - ); - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution_mul_2.clone()], wap, false); - let plmc_ed = plmc_required.accounts().existential_deposits(); - inst.mint_plmc_to(plmc_required.clone()); - inst.mint_plmc_to(plmc_ed); - - let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution_mul_2.clone()], wap); - inst.mint_foreign_asset_to(usdt_required.clone()); - - inst.execute(|| { - assert_ok!(PolimecFunding::contribute( - RuntimeOrigin::signed(BUYER_7), - get_mock_jwt_with_cid( - BUYER_7, - InvestorType::Professional, - generate_did_from_account(BUYER_7), - project_metadata.clone().policy_ipfs_cid.unwrap(), - ), - project_id, - contribution_mul_2.amount, - contribution_mul_2.multiplier, - contribution_mul_2.asset - )); - }); - - inst.finish_funding(project_id, None).unwrap(); - assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); - let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_block); - - // First contribution assertions - let stored_contribution = - inst.execute(|| Contributions::::get((project_id, BUYER_6, 5)).unwrap()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_6); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_6, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_6); - let issuer_usdt_balance = - inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_6, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); - assert_eq!(ct_amount, 0u128); - assert_eq!(issuer_usdt_balance, 0u128); - assert!(unvested_amount.is_none()); - - inst.execute(|| { - assert_ok!(PolimecFunding::settle_failed_contribution( - RuntimeOrigin::signed(BUYER_6), - project_id, - BUYER_6, - 5 - )); - }); - - assert!(inst.execute(|| Contributions::::get((project_id, BUYER_6, 6)).is_none())); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_6); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_6, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_6); - let issuer_usdt_balance = - inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_6, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed() + stored_contribution.plmc_bond); - assert_eq!(plmc_held_amount, 0u128); - assert_eq!(ct_amount, Zero::zero()); - assert_eq!(issuer_usdt_balance, Zero::zero()); - assert!(unvested_amount.is_none()); - inst.assert_migration( - project_id, - BUYER_6, - stored_contribution.ct_amount, - 5, - ParticipationType::Contribution, - false, - ); - - // Second contribution assertions - let stored_contribution = - inst.execute(|| Contributions::::get((project_id, BUYER_7, 6)).unwrap()); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_7); - let issuer_usdt_balance_2 = - inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_7, - HoldReason::Participation(project_id).into(), - ) - }); - assert_eq!(plmc_free_amount, inst.get_ed()); - assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); - assert_eq!(ct_amount, 0u128); - assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance); - assert!(unvested_amount.is_none()); - - inst.execute(|| { - assert_ok!(PolimecFunding::settle_failed_contribution( - RuntimeOrigin::signed(BUYER_7), - project_id, - BUYER_7, - 6 - )); - }); - - assert!(inst.execute(|| Contributions::::get((project_id, BUYER_7, 7)).is_none())); - let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); - let plmc_held_amount = - inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); - let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_7); - let issuer_usdt_balance_2 = - inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); - let unvested_amount = inst.execute(|| { - ::Vesting::total_scheduled_amount( - &BUYER_7, - HoldReason::Participation(project_id).into(), - ) - }); - - assert_eq!(plmc_free_amount, inst.get_ed() + stored_contribution.plmc_bond); - assert_eq!(plmc_held_amount, 0u128); - assert_eq!(ct_amount, Zero::zero()); - assert_eq!(issuer_usdt_balance_2, Zero::zero()); - assert!(unvested_amount.is_none()); - - inst.assert_migration( - project_id, - BUYER_7, - stored_contribution.ct_amount, - 6, - ParticipationType::Contribution, - false, - ); - } - } - - #[cfg(test)] - mod failure { - use super::*; - - #[test] - fn cannot_settle_twice() { - let percentage = 33u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - 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), - project_id, - contributor, - first_contribution.id - )); - assert_noop!( - crate::Pallet::::settle_failed_contribution( - RuntimeOrigin::signed(contributor), - project_id, - contributor, - first_contribution.id - ), - Error::::ParticipationNotFound - ); - }); - } - - #[test] - fn cannot_be_called_on_wrong_outcome() { - let percentage = 100u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); - - let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); - let contributor = first_contribution.contributor; - inst.execute(|| { - assert_noop!( - crate::Pallet::::settle_failed_contribution( - RuntimeOrigin::signed(contributor), - project_id, - contributor, - first_contribution.id - ), - Error::::FundingFailedSettlementNotStarted - ); - }); - } - - #[test] - fn cannot_be_called_before_settlement_started() { - let percentage = 10u64; - let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); - - let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); - let contributor = first_contribution.contributor; - inst.execute(|| { - assert_noop!( - crate::Pallet::::settle_failed_contribution( - RuntimeOrigin::signed(contributor), - project_id, - contributor, - first_contribution.id - ), - Error::::FundingFailedSettlementNotStarted - ); - }); - } - } -} +// use super::*; +// +// #[cfg(test)] +// mod round_flow { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn can_fully_settle_accepted_project() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// 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_total_funding_paid_out(project_id, bids.clone(), contributions.clone()); +// 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] +// fn can_fully_settle_failed_project() { +// let percentage = 33u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// 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_migrations_created(project_id, evaluations, percentage); +// inst.assert_bids_migrations_created(project_id, bids, false); +// inst.assert_contributions_migrations_created(project_id, contributions, false); +// } +// } +// } +// +// #[cfg(test)] +// mod settle_successful_evaluation_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn evaluation_unchanged() { +// let percentage = 89u64; +// +// let (mut inst, project_id) = +// create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding), true); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// let evaluator = first_evaluation.evaluator; +// let prev_balance = inst.get_free_plmc_balance_for(evaluator); +// +// assert_eq!( +// inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, +// EvaluatorsOutcomeOf::::Unchanged +// ); +// +// assert_ok!(inst.execute(|| PolimecFunding::settle_successful_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ))); +// +// let post_balance = inst.get_free_plmc_balance_for(evaluator); +// assert_eq!(post_balance, prev_balance + first_evaluation.current_plmc_bond); +// } +// +// #[test] +// fn evaluation_slashed() { +// let percentage = 50u64; +// let (mut inst, project_id) = +// create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding), true); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// let evaluator = first_evaluation.evaluator; +// let prev_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; +// +// assert_eq!( +// inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, +// EvaluatorsOutcomeOf::::Slashed +// ); +// +// assert_ok!(inst.execute(|| PolimecFunding::settle_successful_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ))); +// +// let post_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; +// assert_eq!( +// post_balance, +// prev_balance + +// (Percent::from_percent(100) - ::EvaluatorSlash::get()) * +// first_evaluation.current_plmc_bond +// ); +// } +// +// #[test] +// fn evaluation_rewarded() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let project_id = inst.create_finished_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// vec![ +// UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_3, 320_000 * USD_UNIT), +// ], +// inst.generate_bids_from_total_ct_percent( +// project_metadata.clone(), +// 50, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ), +// inst.generate_contributions_from_total_ct_percent( +// project_metadata.clone(), +// 50, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ), +// vec![], +// ); +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// // The rewards are calculated as follows: +// // Data: +// // - Funding USD reached: 10_000_000 USD +// // - Total CTs sold: 1_000_000 CT +// // - USD target reached percent: 100% +// +// // Step 1) Calculate the total USD fee: +// // USD fee 1 = 0.1 * 1_000_000 = 100_000 USD +// // USD fee 2 = 0.08 * 4_000_000 = 320_000 USD +// // USD fee 3 = 0.06 * 5_000_000 = 300_000 USD +// // Total USD fee = 100_000 + 320_000 + 300_000 = 720_000 USD +// +// // Step 2) Calculate CT fee as follows: +// // Percent fee = Total USD fee / Funding USD reached = 720_000 / 10_000_000 = 0.072 +// // CT fee = Percent fee * Total CTs sold = 0.072 * 1_000_000 = 72_000 CT +// +// // Step 3) Calculate Early and Normal evaluator reward pots: +// // Total evaluators reward pot = CT fee * 0.3 * USD target reached percent = 72_000 * 0.3 * 1 = 21_600 CT +// // Early evaluators reward pot = Total evaluators reward pot * 0.2 = 21_600 * 0.2 = 4_320 CT +// // Normal evaluators reward pot = Total evaluators reward pot * 0.8 = 21_600 * 0.8 = 17_280 CT +// +// // Step 4) Calculate the early and normal weights of each evaluation: +// // Evaluation 1 = 500_000 USD +// // Evaluation 2 = 250_000 USD +// // Evaluation 3 = 320_000 USD +// +// // Early amount 1 = 500_000 USD +// // Early amount 2 = 250_000 USD +// // Early amount 3 = 250_000 USD +// +// // Total Normal amount = Evaluation 1 + Evaluation 2 + Evaluation 3 = 500_000 + 250_000 + 320_000 = 1_070_000 USD +// // Total Early amount = 10% of USD target = 1_000_000 USD +// +// // Early weight 1 = Early amount 1 / Total Early amount = 500_000 / 1_000_000 = 0.5 +// // Early weight 2 = Early amount 2 / Total Early amount = 250_000 / 1_000_000 = 0.25 +// // Early weight 3 = Early amount 3 / Total Early amount = 250_000 / 1_000_000 = 0.25 +// +// // Normal weight 1 = Evaluation 1 / Total Normal amount = 500_000 / 1_070_000 = 0.467289719626168 +// // Normal weight 2 = Evaluation 2 / Total Normal amount = 250_000 / 1_070_000 = 0.233644859813084 +// // Normal weight 3 = Evaluation 3 / Total Normal amount = 320_000 / 1_070_000 = 0.299065420560748 +// +// // Step 5) Calculate the rewards for each evaluation: +// // Evaluation 1 Early reward = Early weight 1 * Early evaluators reward pot = 0.5 * 4_320 = 2_160 CT +// // Evaluation 2 Early reward = Early weight 2 * Early evaluators reward pot = 0.25 * 4_320 = 1_080 CT +// // Evaluation 3 Early reward = Early weight 3 * Early evaluators reward pot = 0.25 * 4_320 = 1_080 CT +// +// // Evaluation 1 Normal reward = Normal weight 1 * Normal evaluators reward pot = 0.467289719626168 * 17_280 = 8'074.766355140186916 CT +// // Evaluation 2 Normal reward = Normal weight 2 * Normal evaluators reward pot = 0.233644859813084 * 17_280 = 4'037.383177570093458 CT +// // Evaluation 3 Normal reward = Normal weight 3 * Normal evaluators reward pot = 0.299065420560748 * 17_280 = 5'167.850467289719626 CT +// +// // Evaluation 1 Total reward = Evaluation 1 Early reward + Evaluation 1 Normal reward = 2_160 + 8_066 = 10'234.766355140186916 CT +// // Evaluation 2 Total reward = Evaluation 2 Early reward + Evaluation 2 Normal reward = 1_080 + 4_033 = 5'117.383177570093458 CT +// // Evaluation 3 Total reward = Evaluation 3 Early reward + Evaluation 3 Normal reward = 1_080 + 5_201 = 6'247.850467289719626 CT +// +// const EVAL_1_REWARD: u128 = 10_234_766355140186916; +// const EVAL_2_REWARD: u128 = 5_117_383177570093458; +// const EVAL_3_REWARD: u128 = 6_247_850467289719626; +// +// let prev_ct_balances = inst.get_ct_asset_balances_for(project_id, vec![ISSUER_1, ISSUER_2, ISSUER_3]); +// assert!(prev_ct_balances.iter().all(|x| *x == Zero::zero())); +// +// let evals = vec![(EVALUATOR_1, EVAL_1_REWARD), (EVALUATOR_2, EVAL_2_REWARD), (EVALUATOR_3, EVAL_3_REWARD)]; +// +// for (evaluator, expected_reward) in evals { +// let evaluation_locked_plmc = +// inst.get_reserved_plmc_balance_for(evaluator, HoldReason::Evaluation(project_id).into()); +// let free_plmc = inst.get_free_plmc_balance_for(evaluator); +// assert_ok!(inst.execute(|| PolimecFunding::settle_successful_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// evaluator - 21 // The First evaluation index is 0, the first evaluator account is 21 +// ))); +// let ct_rewarded = inst.get_ct_asset_balance_for(project_id, evaluator); +// assert_close_enough!(ct_rewarded, expected_reward, Perquintill::from_float(0.9999)); +// assert_eq!(inst.get_reserved_plmc_balance_for(evaluator, HoldReason::Evaluation(project_id).into()), 0); +// assert_eq!(inst.get_free_plmc_balance_for(evaluator), free_plmc + evaluation_locked_plmc); +// inst.assert_migration(project_id, evaluator, expected_reward, 0, ParticipationType::Evaluation, true); +// } +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn cannot_settle_twice() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// inst.execute(|| { +// let evaluator = first_evaluation.evaluator; +// assert_ok!(crate::Pallet::::settle_successful_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// )); +// assert_noop!( +// crate::Pallet::::settle_successful_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ), +// Error::::ParticipationNotFound +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_on_wrong_outcome() { +// let percentage = 10u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// let evaluator = first_evaluation.evaluator; +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::settle_successful_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ), +// Error::::FundingSuccessSettlementNotStarted +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_before_settlement_started() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// let evaluator = first_evaluation.evaluator; +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::settle_successful_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ), +// Error::::FundingSuccessSettlementNotStarted +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod settle_successful_bid_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn bid_is_correctly_settled() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let issuer = ISSUER_1; +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); +// let bid_1 = BidParams::new(BIDDER_1, 1000 * CT_UNIT, 1, AcceptedFundingAsset::USDT); +// let bid_2 = BidParams::new(BIDDER_2, 1000 * CT_UNIT, 2, AcceptedFundingAsset::USDT); +// +// let community_contributions = inst.generate_contributions_from_total_ct_percent( +// project_metadata.clone(), +// 90, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ); +// +// let project_id = inst.create_finished_project( +// project_metadata.clone(), +// issuer, +// None, +// evaluations, +// vec![bid_1, bid_2], +// community_contributions, +// vec![], +// ); +// +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// // First bid assertions +// let stored_bid = inst.execute(|| Bids::::get((project_id, BIDDER_1, 0)).unwrap()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_1); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_1, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_1); +// let issuer_usdt_balance = +// inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_1, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_bid.plmc_bond); +// assert_eq!(ct_amount, 0u128); +// assert_eq!(issuer_usdt_balance, 0u128); +// assert!(unvested_amount.is_none()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::settle_successful_bid( +// RuntimeOrigin::signed(BIDDER_1), +// project_id, +// BIDDER_1, +// 0 +// )); +// }); +// +// assert!(inst.execute(|| Contributions::::get((project_id, BIDDER_1, 0)).is_none())); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_1); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_1, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_1); +// let issuer_usdt_balance = +// inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_1, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed() + stored_bid.plmc_bond); +// assert_eq!(plmc_held_amount, 0u128); +// assert_eq!(ct_amount, stored_bid.final_ct_amount); +// assert_eq!( +// issuer_usdt_balance, +// stored_bid.final_ct_usd_price.saturating_mul_int(stored_bid.final_ct_amount) +// ); +// assert!(unvested_amount.is_none()); +// inst.assert_migration(project_id, BIDDER_1, stored_bid.final_ct_amount, 0, ParticipationType::Bid, true); +// +// // Second bid assertions +// let stored_bid = inst.execute(|| Bids::::get((project_id, BIDDER_2, 1)).unwrap()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_2); +// let issuer_usdt_balance_2 = +// inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_2, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_bid.plmc_bond); +// assert_eq!(ct_amount, 0u128); +// assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance); +// assert!(unvested_amount.is_none()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::settle_successful_bid( +// RuntimeOrigin::signed(BIDDER_2), +// project_id, +// BIDDER_2, +// 1 +// )); +// }); +// +// assert!(inst.execute(|| Contributions::::get((project_id, BIDDER_2, 1)).is_none())); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_2); +// let issuer_usdt_balance_2 = +// inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst +// .execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_2, +// HoldReason::Participation(project_id).into(), +// ) +// }) +// .unwrap(); +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_bid.plmc_bond); +// assert_eq!(ct_amount, stored_bid.final_ct_amount); +// assert_eq!( +// issuer_usdt_balance_2, +// issuer_usdt_balance + stored_bid.final_ct_usd_price.saturating_mul_int(stored_bid.final_ct_amount) +// ); +// assert_eq!(unvested_amount, stored_bid.plmc_bond); +// +// let vesting_time = stored_bid.multiplier.calculate_vesting_duration::(); +// let now = inst.current_block(); +// inst.jump_to_block(vesting_time + now + 1u64); +// inst.execute(|| { +// assert_ok!(::Vesting::vest( +// RuntimeOrigin::signed(BIDDER_2), +// HoldReason::Participation(project_id).into() +// )); +// }); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_2, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// assert!(unvested_amount.is_none()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); +// assert_eq!(plmc_free_amount, inst.get_ed() + stored_bid.plmc_bond); +// assert_eq!(plmc_held_amount, 0u128); +// inst.assert_migration(project_id, BIDDER_2, stored_bid.final_ct_amount, 1, ParticipationType::Bid, true); +// } +// +// #[test] +// fn rejected_bids_dont_get_vest_schedule() { +// // * Test Setup * +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// // Project variables +// let issuer = ISSUER_1; +// let project_metadata = default_project_metadata(issuer); +// let evaluations = default_evaluations(); +// let auction_token_allocation = +// project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size; +// let mut bids = inst.generate_bids_from_total_usd( +// Percent::from_percent(80) * project_metadata.minimum_price.saturating_mul_int(auction_token_allocation), +// project_metadata.minimum_price, +// vec![60, 40], +// vec![BIDDER_1, BIDDER_2], +// vec![1u8, 1u8], +// ); +// let community_contributions = default_community_buys(); +// +// // Add rejected and accepted bids to test our vesting schedule assertions +// let available_tokens = +// auction_token_allocation.saturating_sub(bids.iter().fold(0, |acc, bid| acc + bid.amount)); +// +// let rejected_bid = vec![BidParams::new(BIDDER_5, available_tokens, 1u8, AcceptedFundingAsset::USDT)]; +// let accepted_bid = vec![BidParams::new(BIDDER_4, available_tokens, 2u8, AcceptedFundingAsset::USDT)]; +// bids.extend(rejected_bid.clone()); +// bids.extend(accepted_bid.clone()); +// +// let project_id = inst.create_auctioning_project(project_metadata.clone(), issuer, None, evaluations); +// +// // Mint the necessary bidding balances +// let bidders_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata.clone(), +// None, +// true, +// ); +// let bidders_existential_deposits = bidders_plmc.accounts().existential_deposits(); +// inst.mint_plmc_to(bidders_plmc.clone()); +// inst.mint_plmc_to(bidders_existential_deposits); +// let bidders_funding_assets = inst +// .calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata.clone(), +// None, +// ); +// inst.mint_foreign_asset_to(bidders_funding_assets); +// +// inst.bid_for_users(project_id, bids).unwrap(); +// +// inst.start_community_funding(project_id).unwrap(); +// +// // Mint the necessary community contribution balances +// let final_price = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// let contributors_plmc = +// inst.calculate_contributed_plmc_spent(community_contributions.clone(), final_price, false); +// let contributors_existential_deposits = contributors_plmc.accounts().existential_deposits(); +// inst.mint_plmc_to(contributors_plmc.clone()); +// inst.mint_plmc_to(contributors_existential_deposits); +// let contributors_funding_assets = +// inst.calculate_contributed_funding_asset_spent(community_contributions.clone(), final_price); +// inst.mint_foreign_asset_to(contributors_funding_assets); +// +// inst.contribute_for_users(project_id, community_contributions).unwrap(); +// +// // Finish and Settle project +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// inst.finish_funding(project_id, None).unwrap(); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.settle_project(project_id).unwrap(); +// +// let plmc_locked_for_accepted_bid = +// inst.calculate_auction_plmc_charged_with_given_price(&accepted_bid, final_price, false); +// let plmc_locked_for_rejected_bid = +// inst.calculate_auction_plmc_charged_with_given_price(&rejected_bid, final_price, false); +// +// let UserToPLMCBalance { account: accepted_user, plmc_amount: accepted_plmc_amount } = +// plmc_locked_for_accepted_bid[0]; +// let UserToPLMCBalance { account: rejected_user, .. } = plmc_locked_for_rejected_bid[0]; +// +// // * Assertions * +// let schedule = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &accepted_user, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// assert_close_enough!(schedule.unwrap(), accepted_plmc_amount, Perquintill::from_float(0.99)); +// assert!(inst +// .execute(|| { +// ::Vesting::total_scheduled_amount( +// &rejected_user, +// HoldReason::Participation(project_id).into(), +// ) +// }) +// .is_none()); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn cannot_settle_twice() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); +// inst.execute(|| { +// let bidder = first_bid.bidder; +// assert_ok!(crate::Pallet::::settle_successful_bid( +// RuntimeOrigin::signed(bidder), +// project_id, +// bidder, +// first_bid.id +// )); +// assert_noop!( +// crate::Pallet::::settle_successful_bid( +// RuntimeOrigin::signed(bidder), +// project_id, +// bidder, +// first_bid.id +// ), +// Error::::ParticipationNotFound +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_on_wrong_outcome() { +// let percentage = 10u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); +// let bidder = first_bid.bidder; +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::settle_successful_bid( +// RuntimeOrigin::signed(bidder), +// project_id, +// bidder, +// first_bid.id +// ), +// Error::::FundingSuccessSettlementNotStarted +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_before_settlement_started() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); +// +// let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); +// let bidder = first_bid.bidder; +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::settle_successful_bid( +// RuntimeOrigin::signed(bidder), +// project_id, +// bidder, +// first_bid.id +// ), +// Error::::FundingSuccessSettlementNotStarted +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod settle_successful_contribution_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn contribution_is_correctly_settled() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let issuer = ISSUER_1; +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); +// let bids = inst.generate_bids_from_total_ct_percent( +// project_metadata.clone(), +// 50, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let mut community_contributions = inst.generate_contributions_from_total_ct_percent( +// project_metadata.clone(), +// 40, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ); +// +// let contribution_mul_1 = +// ContributionParams::::new(BUYER_6, 1000 * CT_UNIT, 1, AcceptedFundingAsset::USDT); +// let contribution_mul_2 = +// ContributionParams::::new(BUYER_7, 1000 * CT_UNIT, 2, AcceptedFundingAsset::USDT); +// +// community_contributions.push(contribution_mul_1); +// +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// issuer, +// None, +// evaluations, +// bids, +// community_contributions, +// ); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution_mul_2.clone()], wap, false); +// let plmc_ed = plmc_required.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_required.clone()); +// inst.mint_plmc_to(plmc_ed); +// +// let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution_mul_2.clone()], wap); +// inst.mint_foreign_asset_to(usdt_required.clone()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_7), +// get_mock_jwt_with_cid( +// BUYER_7, +// InvestorType::Professional, +// generate_did_from_account(BUYER_7), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// project_id, +// contribution_mul_2.amount, +// contribution_mul_2.multiplier, +// contribution_mul_2.asset +// )); +// }); +// +// inst.finish_funding(project_id, None).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful); +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// // First contribution assertions +// let stored_contribution = +// inst.execute(|| Contributions::::get((project_id, BUYER_6, 5)).unwrap()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_6); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_6, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_6); +// let issuer_usdt_balance = +// inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_6, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); +// assert_eq!(ct_amount, 0u128); +// assert_eq!(issuer_usdt_balance, 0u128); +// assert!(unvested_amount.is_none()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::settle_successful_contribution( +// RuntimeOrigin::signed(BUYER_6), +// project_id, +// BUYER_6, +// 5 +// )); +// }); +// +// assert!(inst.execute(|| Contributions::::get((project_id, BUYER_6, 6)).is_none())); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_6); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_6, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_6); +// let issuer_usdt_balance = +// inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_6, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed() + stored_contribution.plmc_bond); +// assert_eq!(plmc_held_amount, 0u128); +// assert_eq!(ct_amount, stored_contribution.ct_amount); +// assert_eq!(issuer_usdt_balance, stored_contribution.usd_contribution_amount); +// assert!(unvested_amount.is_none()); +// inst.assert_migration( +// project_id, +// BUYER_6, +// stored_contribution.ct_amount, +// 5, +// ParticipationType::Contribution, +// true, +// ); +// +// // Second contribution assertions +// let stored_contribution = +// inst.execute(|| Contributions::::get((project_id, BUYER_7, 6)).unwrap()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_7); +// let issuer_usdt_balance_2 = +// inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_7, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); +// assert_eq!(ct_amount, 0u128); +// assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance); +// assert!(unvested_amount.is_none()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::settle_successful_contribution( +// RuntimeOrigin::signed(BUYER_7), +// project_id, +// BUYER_7, +// 6 +// )); +// }); +// +// assert!(inst.execute(|| Contributions::::get((project_id, BUYER_7, 7)).is_none())); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_7); +// let issuer_usdt_balance_2 = +// inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst +// .execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_7, +// HoldReason::Participation(project_id).into(), +// ) +// }) +// .unwrap(); +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); +// assert_eq!(ct_amount, stored_contribution.ct_amount); +// assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance + stored_contribution.usd_contribution_amount); +// assert_eq!(unvested_amount, stored_contribution.plmc_bond); +// +// let vesting_time = stored_contribution.multiplier.calculate_vesting_duration::(); +// let now = inst.current_block(); +// inst.jump_to_block(vesting_time + now + 1u64); +// inst.execute(|| { +// assert_ok!(::Vesting::vest( +// RuntimeOrigin::signed(BUYER_7), +// HoldReason::Participation(project_id).into() +// )); +// }); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_7, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// assert!(unvested_amount.is_none()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); +// assert_eq!(plmc_free_amount, inst.get_ed() + stored_contribution.plmc_bond); +// assert_eq!(plmc_held_amount, 0u128); +// inst.assert_migration( +// project_id, +// BUYER_7, +// stored_contribution.ct_amount, +// 6, +// ParticipationType::Contribution, +// true, +// ); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn cannot_settle_twice() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); +// inst.execute(|| { +// let contributor = first_contribution.contributor; +// assert_ok!(crate::Pallet::::settle_successful_contribution( +// RuntimeOrigin::signed(contributor), +// project_id, +// contributor, +// first_contribution.id +// )); +// assert_noop!( +// crate::Pallet::::settle_successful_contribution( +// RuntimeOrigin::signed(contributor), +// project_id, +// contributor, +// first_contribution.id +// ), +// Error::::ParticipationNotFound +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_on_wrong_outcome() { +// let percentage = 10u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); +// let contributor = first_contribution.contributor; +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::settle_successful_contribution( +// RuntimeOrigin::signed(contributor), +// project_id, +// contributor, +// first_contribution.id +// ), +// Error::::FundingSuccessSettlementNotStarted +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_before_settlement_started() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); +// let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); +// let contributor = first_contribution.contributor; +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::settle_successful_contribution( +// RuntimeOrigin::signed(contributor), +// project_id, +// contributor, +// first_contribution.id +// ), +// Error::::FundingSuccessSettlementNotStarted +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod settle_failed_evaluation_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn evaluation_unchanged() { +// let percentage = 89u64; +// +// let (mut inst, project_id) = +// create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding), true); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// let evaluator = first_evaluation.evaluator; +// let prev_balance = inst.get_free_plmc_balance_for(evaluator); +// +// assert_eq!( +// inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, +// EvaluatorsOutcomeOf::::Unchanged +// ); +// +// assert_ok!(inst.execute(|| PolimecFunding::settle_failed_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ))); +// +// let post_balance = inst.get_free_plmc_balance_for(evaluator); +// assert_eq!(post_balance, prev_balance + first_evaluation.current_plmc_bond); +// } +// +// #[test] +// fn evaluation_slashed() { +// let percentage = 50u64; +// let (mut inst, project_id) = +// create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding), true); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// let evaluator = first_evaluation.evaluator; +// let prev_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; +// +// assert_eq!( +// inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, +// EvaluatorsOutcomeOf::::Slashed +// ); +// +// assert_ok!(inst.execute(|| PolimecFunding::settle_failed_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ))); +// +// let post_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; +// assert_eq!( +// post_balance, +// prev_balance + +// (Percent::from_percent(100) - ::EvaluatorSlash::get()) * +// first_evaluation.current_plmc_bond +// ); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn cannot_settle_twice() { +// let percentage = 33u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// inst.execute(|| { +// let evaluator = first_evaluation.evaluator; +// assert_ok!(crate::Pallet::::settle_failed_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// )); +// assert_noop!( +// crate::Pallet::::settle_failed_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ), +// Error::::ParticipationNotFound +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_on_wrong_outcome() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// let evaluator = first_evaluation.evaluator; +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::settle_failed_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ), +// Error::::FundingFailedSettlementNotStarted +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_before_settlement_started() { +// let percentage = 10u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); +// +// let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); +// let evaluator = first_evaluation.evaluator; +// +// inst.execute(|| { +// assert_noop!( +// PolimecFunding::settle_failed_evaluation( +// RuntimeOrigin::signed(evaluator), +// project_id, +// evaluator, +// first_evaluation.id +// ), +// Error::::FundingFailedSettlementNotStarted +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod settle_failed_bid_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn bid_is_correctly_settled() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let issuer = ISSUER_1; +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); +// let bid_1 = BidParams::new(BIDDER_1, 1000 * CT_UNIT, 1, AcceptedFundingAsset::USDT); +// let bid_2 = BidParams::new(BIDDER_2, 1000 * CT_UNIT, 2, AcceptedFundingAsset::USDT); +// +// let community_contributions = inst.generate_contributions_from_total_ct_percent( +// project_metadata.clone(), +// 20, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ); +// +// let project_id = inst.create_finished_project( +// project_metadata.clone(), +// issuer, +// None, +// evaluations, +// vec![bid_1, bid_2], +// community_contributions, +// vec![], +// ); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// // First bid assertions +// let stored_bid = inst.execute(|| Bids::::get((project_id, BIDDER_1, 0)).unwrap()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_1); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_1, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_1); +// let issuer_usdt_balance = +// inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_1, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_bid.plmc_bond); +// assert_eq!(ct_amount, 0u128); +// assert_eq!(issuer_usdt_balance, 0u128); +// assert!(unvested_amount.is_none()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::settle_failed_bid(RuntimeOrigin::signed(BIDDER_1), project_id, BIDDER_1, 0)); +// }); +// +// assert!(inst.execute(|| Contributions::::get((project_id, BIDDER_1, 0)).is_none())); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_1); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_1, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_1); +// let issuer_usdt_balance = +// inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_1, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed() + stored_bid.plmc_bond); +// assert_eq!(plmc_held_amount, 0u128); +// assert_eq!(ct_amount, Zero::zero()); +// assert_eq!(issuer_usdt_balance, Zero::zero()); +// assert!(unvested_amount.is_none()); +// inst.assert_migration(project_id, BIDDER_1, stored_bid.final_ct_amount, 0, ParticipationType::Bid, false); +// +// // Second bid assertions +// let stored_bid = inst.execute(|| Bids::::get((project_id, BIDDER_2, 1)).unwrap()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_2); +// let issuer_usdt_balance_2 = +// inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_2, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_bid.plmc_bond); +// assert_eq!(ct_amount, 0u128); +// assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance); +// assert!(unvested_amount.is_none()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::settle_failed_bid(RuntimeOrigin::signed(BIDDER_2), project_id, BIDDER_2, 1)); +// }); +// +// assert!(inst.execute(|| Contributions::::get((project_id, BIDDER_2, 1)).is_none())); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BIDDER_2); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BIDDER_2, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BIDDER_2); +// let issuer_usdt_balance_2 = +// inst.get_free_foreign_asset_balance_for(stored_bid.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BIDDER_2, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// assert_eq!(plmc_free_amount, inst.get_ed() + stored_bid.plmc_bond); +// assert_eq!(plmc_held_amount, Zero::zero()); +// assert_eq!(ct_amount, Zero::zero()); +// assert_eq!(issuer_usdt_balance_2, Zero::zero()); +// assert!(unvested_amount.is_none()); +// +// inst.assert_migration(project_id, BIDDER_2, stored_bid.final_ct_amount, 1, ParticipationType::Bid, false); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn cannot_settle_twice() { +// let percentage = 33u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// 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), +// project_id, +// bidder, +// first_bid.id +// )); +// assert_noop!( +// crate::Pallet::::settle_failed_bid( +// RuntimeOrigin::signed(bidder), +// project_id, +// bidder, +// first_bid.id +// ), +// Error::::ParticipationNotFound +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_on_wrong_outcome() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); +// let bidder = first_bid.bidder; +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::settle_failed_bid( +// RuntimeOrigin::signed(bidder), +// project_id, +// bidder, +// first_bid.id +// ), +// Error::::FundingFailedSettlementNotStarted +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_before_settlement_started() { +// let percentage = 10u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); +// +// let first_bid = inst.get_bids(project_id).into_iter().next().unwrap(); +// let bidder = first_bid.bidder; +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::settle_failed_bid( +// RuntimeOrigin::signed(bidder), +// project_id, +// bidder, +// first_bid.id +// ), +// Error::::FundingFailedSettlementNotStarted +// ); +// }); +// } +// } +// } +// +// #[cfg(test)] +// mod settle_failed_contribution_extrinsic { +// use super::*; +// +// #[cfg(test)] +// mod success { +// use super::*; +// +// #[test] +// fn contribution_is_correctly_settled() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let issuer = ISSUER_1; +// let evaluations = +// inst.generate_successful_evaluations(project_metadata.clone(), default_evaluators(), default_weights()); +// let bids = inst.generate_bids_from_total_ct_percent( +// project_metadata.clone(), +// 10, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let mut community_contributions = inst.generate_contributions_from_total_ct_percent( +// project_metadata.clone(), +// 10, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ); +// +// let contribution_mul_1 = +// ContributionParams::::new(BUYER_6, 1000 * CT_UNIT, 1, AcceptedFundingAsset::USDT); +// let contribution_mul_2 = +// ContributionParams::::new(BUYER_7, 1000 * CT_UNIT, 2, AcceptedFundingAsset::USDT); +// +// community_contributions.push(contribution_mul_1); +// +// let project_id = inst.create_remainder_contributing_project( +// project_metadata.clone(), +// issuer, +// None, +// evaluations, +// bids, +// community_contributions, +// ); +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let plmc_required = inst.calculate_contributed_plmc_spent(vec![contribution_mul_2.clone()], wap, false); +// let plmc_ed = plmc_required.accounts().existential_deposits(); +// inst.mint_plmc_to(plmc_required.clone()); +// inst.mint_plmc_to(plmc_ed); +// +// let usdt_required = inst.calculate_contributed_funding_asset_spent(vec![contribution_mul_2.clone()], wap); +// inst.mint_foreign_asset_to(usdt_required.clone()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::contribute( +// RuntimeOrigin::signed(BUYER_7), +// get_mock_jwt_with_cid( +// BUYER_7, +// InvestorType::Professional, +// generate_did_from_account(BUYER_7), +// project_metadata.clone().policy_ipfs_cid.unwrap(), +// ), +// project_id, +// contribution_mul_2.amount, +// contribution_mul_2.multiplier, +// contribution_mul_2.asset +// )); +// }); +// +// inst.finish_funding(project_id, None).unwrap(); +// assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); +// let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_block); +// +// // First contribution assertions +// let stored_contribution = +// inst.execute(|| Contributions::::get((project_id, BUYER_6, 5)).unwrap()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_6); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_6, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_6); +// let issuer_usdt_balance = +// inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_6, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); +// assert_eq!(ct_amount, 0u128); +// assert_eq!(issuer_usdt_balance, 0u128); +// assert!(unvested_amount.is_none()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::settle_failed_contribution( +// RuntimeOrigin::signed(BUYER_6), +// project_id, +// BUYER_6, +// 5 +// )); +// }); +// +// assert!(inst.execute(|| Contributions::::get((project_id, BUYER_6, 6)).is_none())); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_6); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_6, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_6); +// let issuer_usdt_balance = +// inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_6, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed() + stored_contribution.plmc_bond); +// assert_eq!(plmc_held_amount, 0u128); +// assert_eq!(ct_amount, Zero::zero()); +// assert_eq!(issuer_usdt_balance, Zero::zero()); +// assert!(unvested_amount.is_none()); +// inst.assert_migration( +// project_id, +// BUYER_6, +// stored_contribution.ct_amount, +// 5, +// ParticipationType::Contribution, +// false, +// ); +// +// // Second contribution assertions +// let stored_contribution = +// inst.execute(|| Contributions::::get((project_id, BUYER_7, 6)).unwrap()); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_7); +// let issuer_usdt_balance_2 = +// inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_7, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// assert_eq!(plmc_free_amount, inst.get_ed()); +// assert_eq!(plmc_held_amount, stored_contribution.plmc_bond); +// assert_eq!(ct_amount, 0u128); +// assert_eq!(issuer_usdt_balance_2, issuer_usdt_balance); +// assert!(unvested_amount.is_none()); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::settle_failed_contribution( +// RuntimeOrigin::signed(BUYER_7), +// project_id, +// BUYER_7, +// 6 +// )); +// }); +// +// assert!(inst.execute(|| Contributions::::get((project_id, BUYER_7, 7)).is_none())); +// let plmc_free_amount = inst.get_free_plmc_balance_for(BUYER_7); +// let plmc_held_amount = +// inst.get_reserved_plmc_balance_for(BUYER_7, HoldReason::Participation(project_id).into()); +// let ct_amount = inst.get_ct_asset_balance_for(project_id, BUYER_7); +// let issuer_usdt_balance_2 = +// inst.get_free_foreign_asset_balance_for(stored_contribution.funding_asset.to_assethub_id(), issuer); +// let unvested_amount = inst.execute(|| { +// ::Vesting::total_scheduled_amount( +// &BUYER_7, +// HoldReason::Participation(project_id).into(), +// ) +// }); +// +// assert_eq!(plmc_free_amount, inst.get_ed() + stored_contribution.plmc_bond); +// assert_eq!(plmc_held_amount, 0u128); +// assert_eq!(ct_amount, Zero::zero()); +// assert_eq!(issuer_usdt_balance_2, Zero::zero()); +// assert!(unvested_amount.is_none()); +// +// inst.assert_migration( +// project_id, +// BUYER_7, +// stored_contribution.ct_amount, +// 6, +// ParticipationType::Contribution, +// false, +// ); +// } +// } +// +// #[cfg(test)] +// mod failure { +// use super::*; +// +// #[test] +// fn cannot_settle_twice() { +// let percentage = 33u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// 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), +// project_id, +// contributor, +// first_contribution.id +// )); +// assert_noop!( +// crate::Pallet::::settle_failed_contribution( +// RuntimeOrigin::signed(contributor), +// project_id, +// contributor, +// first_contribution.id +// ), +// Error::::ParticipationNotFound +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_on_wrong_outcome() { +// let percentage = 100u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, true); +// +// let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); +// let contributor = first_contribution.contributor; +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::settle_failed_contribution( +// RuntimeOrigin::signed(contributor), +// project_id, +// contributor, +// first_contribution.id +// ), +// Error::::FundingFailedSettlementNotStarted +// ); +// }); +// } +// +// #[test] +// fn cannot_be_called_before_settlement_started() { +// let percentage = 10u64; +// let (mut inst, project_id) = create_project_with_funding_percentage(percentage, None, false); +// +// let first_contribution = inst.get_contributions(project_id).into_iter().next().unwrap(); +// let contributor = first_contribution.contributor; +// inst.execute(|| { +// assert_noop!( +// crate::Pallet::::settle_failed_contribution( +// RuntimeOrigin::signed(contributor), +// project_id, +// contributor, +// first_contribution.id +// ), +// Error::::FundingFailedSettlementNotStarted +// ); +// }); +// } +// } +// } diff --git a/pallets/funding/src/tests/8_ct_migration.rs b/pallets/funding/src/tests/8_ct_migration.rs index bf9349de8..ddeec6ff0 100644 --- a/pallets/funding/src/tests/8_ct_migration.rs +++ b/pallets/funding/src/tests/8_ct_migration.rs @@ -1,289 +1,289 @@ -use super::*; -use frame_support::{assert_err, traits::fungibles::Inspect}; -use sp_runtime::bounded_vec; -use xcm::latest::MaxPalletNameLen; - -mod pallet_migration { - use super::*; - - #[test] - fn start_pallet_migration() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_finished_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.settle_project(project_id).unwrap(); - - inst.execute(|| { - assert_err!( - crate::Pallet::::do_start_pallet_migration( - &EVALUATOR_1, - project_id, - ParaId::from(2006u32), - ), - Error::::NotIssuer - ); - assert_err!( - crate::Pallet::::do_start_pallet_migration(&BIDDER_1, project_id, ParaId::from(2006u32),), - Error::::NotIssuer - ); - assert_err!( - crate::Pallet::::do_start_pallet_migration(&BUYER_1, project_id, ParaId::from(2006u32),), - Error::::NotIssuer - ); - assert_ok!(crate::Pallet::::do_start_pallet_migration( - &ISSUER_1, - project_id, - ParaId::from(2006u32).into(), - )); - }); - - let project_details = inst.get_project_details(project_id); - assert_eq!( - project_details.migration_type, - Some(MigrationType::Pallet(PalletMigrationInfo { - parachain_id: 2006.into(), - hrmp_channel_status: HRMPChannelStatus { - project_to_polimec: ChannelStatus::Closed, - polimec_to_project: ChannelStatus::Closed - }, - migration_readiness_check: None, - })) - ); - assert_eq!(project_details.status, ProjectStatus::CTMigrationStarted); - assert_eq!(inst.execute(|| UnmigratedCounter::::get(project_id)), 10); - } - - fn create_pallet_migration_project(mut inst: MockInstantiator) -> (ProjectId, MockInstantiator) { - let project_id = inst.create_finished_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.settle_project(project_id).unwrap(); - inst.execute(|| { - assert_ok!(crate::Pallet::::do_start_pallet_migration( - &ISSUER_1, - project_id, - ParaId::from(6969u32) - )); - }); - (project_id, inst) - } - - fn fake_hrmp_establishment() { - // Notification sent by the relay when the project starts a project->polimec channel - let open_channel_message = xcm::v3::opaque::Instruction::HrmpNewChannelOpenRequest { - sender: 6969, - max_message_size: 102_300, - max_capacity: 1000, - }; - // This makes Polimec send an acceptance + open channel (polimec->project) message back to the relay - assert_ok!(PolimecFunding::do_handle_channel_open_request(open_channel_message)); - - // Finally the relay notifies the channel polimec->project has been accepted by the project - let channel_accepted_message = xcm::v3::opaque::Instruction::HrmpChannelAccepted { recipient: 6969u32 }; - - // We set the hrmp flags as "Open" and start the receiver pallet check - assert_ok!(PolimecFunding::do_handle_channel_accepted(channel_accepted_message)); - } - - #[test] - fn automatic_hrmp_establishment() { - let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let (project_id, mut inst) = create_pallet_migration_project(inst); - - inst.execute(|| fake_hrmp_establishment()); - - let project_details = inst.get_project_details(project_id); - assert_eq!( - project_details.migration_type, - Some(MigrationType::Pallet(PalletMigrationInfo { - parachain_id: 6969.into(), - hrmp_channel_status: HRMPChannelStatus { - project_to_polimec: ChannelStatus::Open, - polimec_to_project: ChannelStatus::Open - }, - migration_readiness_check: Some(PalletMigrationReadinessCheck { - holding_check: (0, CheckOutcome::AwaitingResponse), - pallet_check: (1, CheckOutcome::AwaitingResponse) - }), - })) - ); - } - - /// Check that the polimec sovereign account has the ct issuance on the project chain, and the receiver pallet is in - /// the runtime. - #[test] - fn pallet_readiness_check() { - let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let (project_id, mut inst) = create_pallet_migration_project(inst); - inst.execute(|| fake_hrmp_establishment()); - - // At this point, we sent the pallet check xcm to the project chain, and we are awaiting a query response message. - // query id 0 is the CT balance of the Polimec SA - // query id 1 is the existence of the receiver pallet - - // We simulate the response from the project chain - let ct_issuance = - inst.execute(|| ::ContributionTokenCurrency::total_issuance(project_id)); - let ct_multiassets: MultiAssets = vec![MultiAsset { - id: Concrete(MultiLocation { parents: 1, interior: X1(Parachain(6969)) }), - fun: Fungibility::Fungible(ct_issuance), - }] - .into(); - - inst.execute(|| { - assert_ok!(PolimecFunding::do_pallet_migration_readiness_response( - MultiLocation::new(1u8, X1(Parachain(6969u32))), - 0u64, - Response::Assets(ct_multiassets), - )); - }); - - let module_name: BoundedVec = - BoundedVec::try_from("polimec_receiver".as_bytes().to_vec()).unwrap(); - let pallet_info = xcm::latest::PalletInfo { - // index is used for future `Transact` calls to the pallet for migrating a user - index: 69, - // Doesn't matter - name: module_name.clone(), - // Main check that the receiver pallet is there - module_name, - // These might be useful in the future, but not for now - major: 0, - minor: 0, - patch: 0, - }; - inst.execute(|| { - assert_ok!(PolimecFunding::do_pallet_migration_readiness_response( - MultiLocation::new(1u8, X1(Parachain(6969u32))), - 1u64, - Response::PalletsInfo(bounded_vec![pallet_info]), - )); - }); - - let project_details = inst.get_project_details(project_id); - if let MigrationType::Pallet(info) = project_details.migration_type.unwrap() { - assert_eq!(info.migration_readiness_check.unwrap().holding_check.1, CheckOutcome::Passed(None)); - assert_eq!(info.migration_readiness_check.unwrap().pallet_check.1, CheckOutcome::Passed(Some(69))); - } else { - panic!("Migration type is not Pallet") - } - } -} - -mod offchain_migration { - use super::*; - - #[test] - fn start_offchain_migration() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - // Create migrations for 2 projects, to check the `remaining_participants` is unaffected by other projects - let project_id = inst.create_finished_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.settle_project(project_id).unwrap(); - - let project_id = inst.create_finished_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.settle_project(project_id).unwrap(); - - inst.execute(|| { - assert_err!( - crate::Pallet::::do_start_offchain_migration(project_id, EVALUATOR_1,), - Error::::NotIssuer - ); - - assert_ok!(crate::Pallet::::do_start_offchain_migration(project_id, ISSUER_1,)); - }); - - let project_details = inst.get_project_details(project_id); - assert_eq!(inst.execute(|| UnmigratedCounter::::get(project_id)), 10); - assert_eq!(project_details.status, ProjectStatus::CTMigrationStarted); - } - - fn create_offchain_migration_project(mut inst: MockInstantiator) -> (ProjectId, MockInstantiator) { - let project_id = inst.create_finished_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); - inst.settle_project(project_id).unwrap(); - inst.execute(|| { - assert_ok!(crate::Pallet::::do_start_offchain_migration(project_id, ISSUER_1,)); - }); - (project_id, inst) - } - - #[test] - fn confirm_offchain_migration() { - let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let (project_id, mut inst) = create_offchain_migration_project(inst); - - let bidder_1_migrations = inst.execute(|| UserMigrations::::get((project_id, BIDDER_1))).unwrap(); - assert_eq!(bidder_1_migrations.0, MigrationStatus::NotStarted); - - inst.execute(|| { - assert_ok!(crate::Pallet::::do_confirm_offchain_migration(project_id, ISSUER_1, BIDDER_1)); - }); - - let bidder_1_migrations = inst.execute(|| UserMigrations::::get((project_id, BIDDER_1))).unwrap(); - assert_eq!(bidder_1_migrations.0, MigrationStatus::Confirmed); - } - - #[test] - fn mark_project_migration_as_finished() { - let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let (project_id, mut inst) = create_offchain_migration_project(inst); - - let participants = inst.execute(|| UserMigrations::::iter_key_prefix((project_id,)).collect_vec()); - for participant in participants { - inst.execute(|| { - assert_ok!(crate::Pallet::::do_confirm_offchain_migration( - project_id, - ISSUER_1, - participant - )); - }); - } - - inst.execute(|| { - assert_ok!(crate::Pallet::::do_mark_project_ct_migration_as_finished(project_id)); - }); - } - - // Can't start if project is not settled -} +// use super::*; +// use frame_support::{assert_err, traits::fungibles::Inspect}; +// use sp_runtime::bounded_vec; +// use xcm::latest::MaxPalletNameLen; +// +// mod pallet_migration { +// use super::*; +// +// #[test] +// fn start_pallet_migration() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id = inst.create_finished_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.settle_project(project_id).unwrap(); +// +// inst.execute(|| { +// assert_err!( +// crate::Pallet::::do_start_pallet_migration( +// &EVALUATOR_1, +// project_id, +// ParaId::from(2006u32), +// ), +// Error::::NotIssuer +// ); +// assert_err!( +// crate::Pallet::::do_start_pallet_migration(&BIDDER_1, project_id, ParaId::from(2006u32),), +// Error::::NotIssuer +// ); +// assert_err!( +// crate::Pallet::::do_start_pallet_migration(&BUYER_1, project_id, ParaId::from(2006u32),), +// Error::::NotIssuer +// ); +// assert_ok!(crate::Pallet::::do_start_pallet_migration( +// &ISSUER_1, +// project_id, +// ParaId::from(2006u32).into(), +// )); +// }); +// +// let project_details = inst.get_project_details(project_id); +// assert_eq!( +// project_details.migration_type, +// Some(MigrationType::Pallet(PalletMigrationInfo { +// parachain_id: 2006.into(), +// hrmp_channel_status: HRMPChannelStatus { +// project_to_polimec: ChannelStatus::Closed, +// polimec_to_project: ChannelStatus::Closed +// }, +// migration_readiness_check: None, +// })) +// ); +// assert_eq!(project_details.status, ProjectStatus::CTMigrationStarted); +// assert_eq!(inst.execute(|| UnmigratedCounter::::get(project_id)), 10); +// } +// +// fn create_pallet_migration_project(mut inst: MockInstantiator) -> (ProjectId, MockInstantiator) { +// let project_id = inst.create_finished_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.settle_project(project_id).unwrap(); +// inst.execute(|| { +// assert_ok!(crate::Pallet::::do_start_pallet_migration( +// &ISSUER_1, +// project_id, +// ParaId::from(6969u32) +// )); +// }); +// (project_id, inst) +// } +// +// fn fake_hrmp_establishment() { +// // Notification sent by the relay when the project starts a project->polimec channel +// let open_channel_message = xcm::v3::opaque::Instruction::HrmpNewChannelOpenRequest { +// sender: 6969, +// max_message_size: 102_300, +// max_capacity: 1000, +// }; +// // This makes Polimec send an acceptance + open channel (polimec->project) message back to the relay +// assert_ok!(PolimecFunding::do_handle_channel_open_request(open_channel_message)); +// +// // Finally the relay notifies the channel polimec->project has been accepted by the project +// let channel_accepted_message = xcm::v3::opaque::Instruction::HrmpChannelAccepted { recipient: 6969u32 }; +// +// // We set the hrmp flags as "Open" and start the receiver pallet check +// assert_ok!(PolimecFunding::do_handle_channel_accepted(channel_accepted_message)); +// } +// +// #[test] +// fn automatic_hrmp_establishment() { +// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let (project_id, mut inst) = create_pallet_migration_project(inst); +// +// inst.execute(|| fake_hrmp_establishment()); +// +// let project_details = inst.get_project_details(project_id); +// assert_eq!( +// project_details.migration_type, +// Some(MigrationType::Pallet(PalletMigrationInfo { +// parachain_id: 6969.into(), +// hrmp_channel_status: HRMPChannelStatus { +// project_to_polimec: ChannelStatus::Open, +// polimec_to_project: ChannelStatus::Open +// }, +// migration_readiness_check: Some(PalletMigrationReadinessCheck { +// holding_check: (0, CheckOutcome::AwaitingResponse), +// pallet_check: (1, CheckOutcome::AwaitingResponse) +// }), +// })) +// ); +// } +// +// /// Check that the polimec sovereign account has the ct issuance on the project chain, and the receiver pallet is in +// /// the runtime. +// #[test] +// fn pallet_readiness_check() { +// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let (project_id, mut inst) = create_pallet_migration_project(inst); +// inst.execute(|| fake_hrmp_establishment()); +// +// // At this point, we sent the pallet check xcm to the project chain, and we are awaiting a query response message. +// // query id 0 is the CT balance of the Polimec SA +// // query id 1 is the existence of the receiver pallet +// +// // We simulate the response from the project chain +// let ct_issuance = +// inst.execute(|| ::ContributionTokenCurrency::total_issuance(project_id)); +// let ct_multiassets: MultiAssets = vec![MultiAsset { +// id: Concrete(MultiLocation { parents: 1, interior: X1(Parachain(6969)) }), +// fun: Fungibility::Fungible(ct_issuance), +// }] +// .into(); +// +// inst.execute(|| { +// assert_ok!(PolimecFunding::do_pallet_migration_readiness_response( +// MultiLocation::new(1u8, X1(Parachain(6969u32))), +// 0u64, +// Response::Assets(ct_multiassets), +// )); +// }); +// +// let module_name: BoundedVec = +// BoundedVec::try_from("polimec_receiver".as_bytes().to_vec()).unwrap(); +// let pallet_info = xcm::latest::PalletInfo { +// // index is used for future `Transact` calls to the pallet for migrating a user +// index: 69, +// // Doesn't matter +// name: module_name.clone(), +// // Main check that the receiver pallet is there +// module_name, +// // These might be useful in the future, but not for now +// major: 0, +// minor: 0, +// patch: 0, +// }; +// inst.execute(|| { +// assert_ok!(PolimecFunding::do_pallet_migration_readiness_response( +// MultiLocation::new(1u8, X1(Parachain(6969u32))), +// 1u64, +// Response::PalletsInfo(bounded_vec![pallet_info]), +// )); +// }); +// +// let project_details = inst.get_project_details(project_id); +// if let MigrationType::Pallet(info) = project_details.migration_type.unwrap() { +// assert_eq!(info.migration_readiness_check.unwrap().holding_check.1, CheckOutcome::Passed(None)); +// assert_eq!(info.migration_readiness_check.unwrap().pallet_check.1, CheckOutcome::Passed(Some(69))); +// } else { +// panic!("Migration type is not Pallet") +// } +// } +// } +// +// mod offchain_migration { +// use super::*; +// +// #[test] +// fn start_offchain_migration() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// // Create migrations for 2 projects, to check the `remaining_participants` is unaffected by other projects +// let project_id = inst.create_finished_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.settle_project(project_id).unwrap(); +// +// let project_id = inst.create_finished_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.settle_project(project_id).unwrap(); +// +// inst.execute(|| { +// assert_err!( +// crate::Pallet::::do_start_offchain_migration(project_id, EVALUATOR_1,), +// Error::::NotIssuer +// ); +// +// assert_ok!(crate::Pallet::::do_start_offchain_migration(project_id, ISSUER_1,)); +// }); +// +// let project_details = inst.get_project_details(project_id); +// assert_eq!(inst.execute(|| UnmigratedCounter::::get(project_id)), 10); +// assert_eq!(project_details.status, ProjectStatus::CTMigrationStarted); +// } +// +// fn create_offchain_migration_project(mut inst: MockInstantiator) -> (ProjectId, MockInstantiator) { +// let project_id = inst.create_finished_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// inst.advance_time(::SuccessToSettlementTime::get()).unwrap(); +// inst.settle_project(project_id).unwrap(); +// inst.execute(|| { +// assert_ok!(crate::Pallet::::do_start_offchain_migration(project_id, ISSUER_1,)); +// }); +// (project_id, inst) +// } +// +// #[test] +// fn confirm_offchain_migration() { +// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let (project_id, mut inst) = create_offchain_migration_project(inst); +// +// let bidder_1_migrations = inst.execute(|| UserMigrations::::get((project_id, BIDDER_1))).unwrap(); +// assert_eq!(bidder_1_migrations.0, MigrationStatus::NotStarted); +// +// inst.execute(|| { +// assert_ok!(crate::Pallet::::do_confirm_offchain_migration(project_id, ISSUER_1, BIDDER_1)); +// }); +// +// let bidder_1_migrations = inst.execute(|| UserMigrations::::get((project_id, BIDDER_1))).unwrap(); +// assert_eq!(bidder_1_migrations.0, MigrationStatus::Confirmed); +// } +// +// #[test] +// fn mark_project_migration_as_finished() { +// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let (project_id, mut inst) = create_offchain_migration_project(inst); +// +// let participants = inst.execute(|| UserMigrations::::iter_key_prefix((project_id,)).collect_vec()); +// for participant in participants { +// inst.execute(|| { +// assert_ok!(crate::Pallet::::do_confirm_offchain_migration( +// project_id, +// ISSUER_1, +// participant +// )); +// }); +// } +// +// inst.execute(|| { +// assert_ok!(crate::Pallet::::do_mark_project_ct_migration_as_finished(project_id)); +// }); +// } +// +// // Can't start if project is not settled +// } diff --git a/pallets/funding/src/tests/misc.rs b/pallets/funding/src/tests/misc.rs index 602680a52..0e8f47c0c 100644 --- a/pallets/funding/src/tests/misc.rs +++ b/pallets/funding/src/tests/misc.rs @@ -1,368 +1,368 @@ -use super::*; - -// check that functions created to facilitate testing return the expected results -mod helper_functions { - use super::*; - use polimec_common::USD_DECIMALS; - - #[test] - fn test_usd_price_decimal_aware() { - let submitted_price = FixedU128::from_float(1.85); - let asset_decimals = 4; - let expected_price = FixedU128::from_float(185.0); - type PriceProvider = ::PriceProvider; - assert_eq!( - PriceProvider::calculate_decimals_aware_price(submitted_price, USD_DECIMALS, asset_decimals).unwrap(), - expected_price - ); - - let submitted_price = FixedU128::from_float(1.0); - let asset_decimals = 12; - let expected_price = FixedU128::from_float(0.000001); - - assert_eq!( - PriceProvider::calculate_decimals_aware_price(submitted_price, USD_DECIMALS, asset_decimals).unwrap(), - expected_price - ); - } - - #[test] - fn test_convert_from_decimal_aware_back_to_normal() { - // Test with an asset with less decimals than USD - let original_price = FixedU128::from_float(1.85); - let asset_decimals = 4; - let decimal_aware = ::PriceProvider::calculate_decimals_aware_price( - original_price, - USD_DECIMALS, - asset_decimals, - ) - .unwrap(); - let converted_back = ::PriceProvider::convert_back_to_normal_price( - decimal_aware, - USD_DECIMALS, - asset_decimals, - ) - .unwrap(); - assert_eq!(converted_back, original_price); - - // Test with an asset with more decimals than USD - let original_price = FixedU128::from_float(1.85); - let asset_decimals = 12; - let decimal_aware = ::PriceProvider::calculate_decimals_aware_price( - original_price, - USD_DECIMALS, - asset_decimals, - ) - .unwrap(); - let converted_back = ::PriceProvider::convert_back_to_normal_price( - decimal_aware, - USD_DECIMALS, - asset_decimals, - ) - .unwrap(); - assert_eq!(converted_back, original_price); - } - - #[test] - fn calculate_evaluation_plmc_spent() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - const EVALUATOR_1: AccountIdOf = 1u32; - const USD_AMOUNT_1: BalanceOf = 150_000 * USD_UNIT; - const EXPECTED_PLMC_AMOUNT_1: f64 = 17_857.1428571428f64; - - const EVALUATOR_2: AccountIdOf = 2u32; - const USD_AMOUNT_2: BalanceOf = 50_000 * USD_UNIT; - const EXPECTED_PLMC_AMOUNT_2: f64 = 5_952.3809523809f64; - - const EVALUATOR_3: AccountIdOf = 3u32; - const USD_AMOUNT_3: BalanceOf = 75_000 * USD_UNIT; - const EXPECTED_PLMC_AMOUNT_3: f64 = 8_928.5714285714f64; - - const EVALUATOR_4: AccountIdOf = 4u32; - const USD_AMOUNT_4: BalanceOf = 100 * USD_UNIT; - const EXPECTED_PLMC_AMOUNT_4: f64 = 11.9047619047f64; - - const EVALUATOR_5: AccountIdOf = 5u32; - - // 123.7 USD - const USD_AMOUNT_5: BalanceOf = 1237 * USD_UNIT / 10; - const EXPECTED_PLMC_AMOUNT_5: f64 = 14.7261904761f64; - - const PLMC_PRICE: f64 = 8.4f64; - - assert_eq!( - ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap(), - PriceOf::::from_float(PLMC_PRICE) - ); - - let evaluations = vec![ - UserToUSDBalance::::new(EVALUATOR_1, USD_AMOUNT_1), - UserToUSDBalance::::new(EVALUATOR_2, USD_AMOUNT_2), - UserToUSDBalance::::new(EVALUATOR_3, USD_AMOUNT_3), - UserToUSDBalance::::new(EVALUATOR_4, USD_AMOUNT_4), - UserToUSDBalance::::new(EVALUATOR_5, USD_AMOUNT_5), - ]; - - let expected_plmc_spent = vec![ - (EVALUATOR_1, EXPECTED_PLMC_AMOUNT_1), - (EVALUATOR_2, EXPECTED_PLMC_AMOUNT_2), - (EVALUATOR_3, EXPECTED_PLMC_AMOUNT_3), - (EVALUATOR_4, EXPECTED_PLMC_AMOUNT_4), - (EVALUATOR_5, EXPECTED_PLMC_AMOUNT_5), - ]; - - let calculated_plmc_spent = inst - .calculate_evaluation_plmc_spent(evaluations, false) - .into_iter() - .sorted_by(|a, b| a.account.cmp(&b.account)) - .map(|map| map.plmc_amount) - .collect_vec(); - let expected_plmc_spent = expected_plmc_spent - .into_iter() - .sorted_by(|a, b| a.0.cmp(&b.0)) - .map(|map| { - let f64_amount = map.1; - let fixed_amount = FixedU128::from_float(f64_amount); - fixed_amount.checked_mul_int(PLMC).unwrap() - }) - .collect_vec(); - for (expected, calculated) in zip(expected_plmc_spent, calculated_plmc_spent) { - assert_close_enough!(expected, calculated, Perquintill::from_float(0.999)); - } - } - - #[test] - fn calculate_auction_plmc_returned() { - const CT_AMOUNT_1: u128 = 5000 * CT_UNIT; - const CT_AMOUNT_2: u128 = 40_000 * CT_UNIT; - const CT_AMOUNT_3: u128 = 10_000 * CT_UNIT; - const CT_AMOUNT_4: u128 = 6000 * CT_UNIT; - const CT_AMOUNT_5: u128 = 2000 * CT_UNIT; - - let bid_1 = BidParams::new(BIDDER_1, CT_AMOUNT_1, 1u8, AcceptedFundingAsset::USDT); - let bid_2 = BidParams::new(BIDDER_2, CT_AMOUNT_2, 1u8, AcceptedFundingAsset::USDT); - let bid_3 = BidParams::new(BIDDER_1, CT_AMOUNT_3, 1u8, AcceptedFundingAsset::USDT); - let bid_4 = BidParams::new(BIDDER_3, CT_AMOUNT_4, 1u8, AcceptedFundingAsset::USDT); - let bid_5 = BidParams::new(BIDDER_4, CT_AMOUNT_5, 1u8, AcceptedFundingAsset::USDT); - - // post bucketing, the bids look like this: - // (BIDDER_1, 5k) - (BIDDER_2, 40k) - (BIDDER_1, 5k) - (BIDDER_1, 5k) - (BIDDER_3 - 5k) - (BIDDER_3 - 1k) - (BIDDER_4 - 2k) - // | -------------------- 1USD ----------------------|---- 1.1 USD ---|---- 1.2 USD ----|----------- 1.3 USD -------------| - // post wap ~ 1.0557252: - // (Accepted, 5k) - (Partially, 32k) - (Rejected, 5k) - (Accepted, 5k) - (Accepted - 5k) - (Accepted - 1k) - (Accepted - 2k) - - const ORIGINAL_PLMC_CHARGED_BIDDER_1: f64 = 18_452.3809523790; - const ORIGINAL_PLMC_CHARGED_BIDDER_2: f64 = 47_619.0476190470; - const ORIGINAL_PLMC_CHARGED_BIDDER_3: f64 = 86_90.4761904760; - const ORIGINAL_PLMC_CHARGED_BIDDER_4: f64 = 30_95.2380952380; - - const FINAL_PLMC_CHARGED_BIDDER_1: f64 = 12_236.4594692840; - const FINAL_PLMC_CHARGED_BIDDER_2: f64 = 38_095.2380952380; - const FINAL_PLMC_CHARGED_BIDDER_3: f64 = 75_40.8942202840; - const FINAL_PLMC_CHARGED_BIDDER_4: f64 = 2_513.6314067610; - - let bids = vec![bid_1, bid_2, bid_3, bid_4, bid_5]; - - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = ProjectMetadata { - token_information: default_token_information(), - mainnet_token_max_supply: 8_000_000 * CT_UNIT, - total_allocation_size: 100_000 * CT_UNIT, - auction_round_allocation_percentage: Percent::from_percent(50u8), - minimum_price: PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(10.0), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(), - bidding_ticket_sizes: BiddingTicketSizes { - professional: TicketSize::new(5000 * USD_UNIT, None), - institutional: TicketSize::new(5000 * USD_UNIT, None), - phantom: Default::default(), - }, - contributing_ticket_sizes: ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, None), - professional: TicketSize::new(USD_UNIT, None), - institutional: TicketSize::new(USD_UNIT, None), - phantom: Default::default(), - }, - participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), - funding_destination_account: ISSUER_1, - policy_ipfs_cid: Some(ipfs_hash()), - }; - - let project_id = inst.create_community_contributing_project( - project_metadata.clone(), - ISSUER_1, - None, - default_evaluations(), - bids.clone(), - ); - - let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); - - let expected_returns = vec![ - ORIGINAL_PLMC_CHARGED_BIDDER_1 - FINAL_PLMC_CHARGED_BIDDER_1, - ORIGINAL_PLMC_CHARGED_BIDDER_2 - FINAL_PLMC_CHARGED_BIDDER_2, - ORIGINAL_PLMC_CHARGED_BIDDER_3 - FINAL_PLMC_CHARGED_BIDDER_3, - ORIGINAL_PLMC_CHARGED_BIDDER_4 - FINAL_PLMC_CHARGED_BIDDER_4, - ]; - - let mut returned_plmc_mappings = - inst.calculate_auction_plmc_returned_from_all_bids_made(&bids, project_metadata.clone(), wap); - returned_plmc_mappings.sort_by(|b1, b2| b1.account.cmp(&b2.account)); - - let returned_plmc_balances = returned_plmc_mappings.into_iter().map(|map| map.plmc_amount).collect_vec(); - - for (expected_return, returned_balance) in zip(expected_returns, returned_plmc_balances) { - let expected_value = FixedU128::from_float(expected_return).checked_mul_int(PLMC).unwrap(); - - assert_close_enough!(expected_value, returned_balance, Perquintill::from_float(0.99)); - } - } - - #[test] - fn bucket_wap_calculation() { - let initial_price = FixedU128::from_float(10.0); - let mut bucket = Bucket::new(100u32, initial_price, FixedU128::from_float(1.0), 10u32); - let wap = bucket.calculate_wap(100u32); - assert!(wap == initial_price); - - // Initial token amount: 100 - // Simulate total bidding amount of 128 - bucket.update(100u32); - bucket.update(10u32); - bucket.update(10u32); - bucket.update(8u32); - let wap = bucket.calculate_wap(100u32); - let expected = FixedU128::from_float(10.628); - let diff = if wap > expected { wap - expected } else { expected - wap }; - assert!(diff <= FixedU128::from_float(0.001)); - } - - #[test] - fn calculate_contributed_plmc_spent() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - const PLMC_PRICE: f64 = 8.4f64; - const CT_PRICE: f64 = 16.32f64; - - const CONTRIBUTOR_1: AccountIdOf = 1u32; - const TOKEN_AMOUNT_1: u128 = 120 * CT_UNIT; - const MULTIPLIER_1: u8 = 1u8; - const _TICKET_SIZE_USD_1: u128 = 1_958_4_000_000_000_u128; - const EXPECTED_PLMC_AMOUNT_1: f64 = 233.1_428_571_428f64; - - const CONTRIBUTOR_2: AccountIdOf = 2u32; - const TOKEN_AMOUNT_2: u128 = 5023 * CT_UNIT; - const MULTIPLIER_2: u8 = 2u8; - const _TICKET_SIZE_USD_2: u128 = 81_975_3_600_000_000_u128; - const EXPECTED_PLMC_AMOUNT_2: f64 = 4_879.4_857_142_857f64; - - const CONTRIBUTOR_3: AccountIdOf = 3u32; - const TOKEN_AMOUNT_3: u128 = 20_000 * CT_UNIT; - const MULTIPLIER_3: u8 = 17u8; - const _TICKET_SIZE_USD_3: u128 = 326_400_0_000_000_000_u128; - const EXPECTED_PLMC_AMOUNT_3: f64 = 2_285.7_142_857_142f64; - - const CONTRIBUTOR_4: AccountIdOf = 4u32; - const TOKEN_AMOUNT_4: u128 = 1_000_000 * CT_UNIT; - const MULTIPLIER_4: u8 = 25u8; - const _TICKET_SIZE_4: u128 = 16_320_000_0_000_000_000_u128; - const EXPECTED_PLMC_AMOUNT_4: f64 = 77_714.2_857_142_857f64; - - const CONTRIBUTOR_5: AccountIdOf = 5u32; - // 0.1233 CTs - const TOKEN_AMOUNT_5: u128 = 1_233 * CT_UNIT / 10_000; - const MULTIPLIER_5: u8 = 10u8; - const _TICKET_SIZE_5: u128 = 2_0_122_562_000_u128; - const EXPECTED_PLMC_AMOUNT_5: f64 = 0.0_239_554_285f64; - - assert_eq!( - ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap(), - PriceOf::::from_float(PLMC_PRICE) - ); - - let contributions = vec![ - ContributionParams::new(CONTRIBUTOR_1, TOKEN_AMOUNT_1, MULTIPLIER_1, AcceptedFundingAsset::USDT), - ContributionParams::new(CONTRIBUTOR_2, TOKEN_AMOUNT_2, MULTIPLIER_2, AcceptedFundingAsset::USDT), - ContributionParams::new(CONTRIBUTOR_3, TOKEN_AMOUNT_3, MULTIPLIER_3, AcceptedFundingAsset::USDT), - ContributionParams::new(CONTRIBUTOR_4, TOKEN_AMOUNT_4, MULTIPLIER_4, AcceptedFundingAsset::USDT), - ContributionParams::new(CONTRIBUTOR_5, TOKEN_AMOUNT_5, MULTIPLIER_5, AcceptedFundingAsset::USDT), - ]; - - let expected_plmc_spent = vec![ - (CONTRIBUTOR_1, EXPECTED_PLMC_AMOUNT_1), - (CONTRIBUTOR_2, EXPECTED_PLMC_AMOUNT_2), - (CONTRIBUTOR_3, EXPECTED_PLMC_AMOUNT_3), - (CONTRIBUTOR_4, EXPECTED_PLMC_AMOUNT_4), - (CONTRIBUTOR_5, EXPECTED_PLMC_AMOUNT_5), - ]; - - let calculated_plmc_spent = inst - .calculate_contributed_plmc_spent( - contributions, - PriceProviderOf::::calculate_decimals_aware_price( - PriceOf::::from_float(CT_PRICE), - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(), - false, - ) - .into_iter() - .sorted_by(|a, b| a.account.cmp(&b.account)) - .map(|map| map.plmc_amount) - .collect_vec(); - let expected_plmc_spent = expected_plmc_spent - .into_iter() - .sorted_by(|a, b| a.0.cmp(&b.0)) - .map(|map| { - let fixed_amount = FixedU128::from_float(map.1); - fixed_amount.checked_mul_int(PLMC).unwrap() - }) - .collect_vec(); - for (expected, calculated) in zip(expected_plmc_spent, calculated_plmc_spent) { - assert_close_enough!(expected, calculated, Perquintill::from_float(0.999)); - } - } -} - -// logic of small functions that extrinsics use to process data or interact with storage -mod inner_functions { - use super::*; - - #[test] - fn calculate_vesting_duration() { - let default_multiplier = MultiplierOf::::default(); - let default_multiplier_duration = default_multiplier.calculate_vesting_duration::(); - assert_eq!(default_multiplier_duration, 1u64); - - let multiplier_1 = MultiplierOf::::new(1u8).unwrap(); - let multiplier_1_duration = multiplier_1.calculate_vesting_duration::(); - assert_eq!(multiplier_1_duration, 1u64); - - let multiplier_2 = MultiplierOf::::new(2u8).unwrap(); - let multiplier_2_duration = multiplier_2.calculate_vesting_duration::(); - assert_eq!(multiplier_2_duration, FixedU128::from_rational(2167, 1000).saturating_mul_int((DAYS * 7) as u64)); - - let multiplier_3 = MultiplierOf::::new(3u8).unwrap(); - let multiplier_3_duration = multiplier_3.calculate_vesting_duration::(); - assert_eq!(multiplier_3_duration, FixedU128::from_rational(4334, 1000).saturating_mul_int((DAYS * 7) as u64)); - - let multiplier_19 = MultiplierOf::::new(19u8).unwrap(); - let multiplier_19_duration = multiplier_19.calculate_vesting_duration::(); - assert_eq!(multiplier_19_duration, FixedU128::from_rational(39006, 1000).saturating_mul_int((DAYS * 7) as u64)); - - let multiplier_20 = MultiplierOf::::new(20u8).unwrap(); - let multiplier_20_duration = multiplier_20.calculate_vesting_duration::(); - assert_eq!(multiplier_20_duration, FixedU128::from_rational(41173, 1000).saturating_mul_int((DAYS * 7) as u64)); - - let multiplier_24 = MultiplierOf::::new(24u8).unwrap(); - let multiplier_24_duration = multiplier_24.calculate_vesting_duration::(); - assert_eq!(multiplier_24_duration, FixedU128::from_rational(49841, 1000).saturating_mul_int((DAYS * 7) as u64)); - - let multiplier_25 = MultiplierOf::::new(25u8).unwrap(); - let multiplier_25_duration = multiplier_25.calculate_vesting_duration::(); - assert_eq!(multiplier_25_duration, FixedU128::from_rational(52008, 1000).saturating_mul_int((DAYS * 7) as u64)); - } -} +// use super::*; +// +// // check that functions created to facilitate testing return the expected results +// mod helper_functions { +// use super::*; +// use polimec_common::USD_DECIMALS; +// +// #[test] +// fn test_usd_price_decimal_aware() { +// let submitted_price = FixedU128::from_float(1.85); +// let asset_decimals = 4; +// let expected_price = FixedU128::from_float(185.0); +// type PriceProvider = ::PriceProvider; +// assert_eq!( +// PriceProvider::calculate_decimals_aware_price(submitted_price, USD_DECIMALS, asset_decimals).unwrap(), +// expected_price +// ); +// +// let submitted_price = FixedU128::from_float(1.0); +// let asset_decimals = 12; +// let expected_price = FixedU128::from_float(0.000001); +// +// assert_eq!( +// PriceProvider::calculate_decimals_aware_price(submitted_price, USD_DECIMALS, asset_decimals).unwrap(), +// expected_price +// ); +// } +// +// #[test] +// fn test_convert_from_decimal_aware_back_to_normal() { +// // Test with an asset with less decimals than USD +// let original_price = FixedU128::from_float(1.85); +// let asset_decimals = 4; +// let decimal_aware = ::PriceProvider::calculate_decimals_aware_price( +// original_price, +// USD_DECIMALS, +// asset_decimals, +// ) +// .unwrap(); +// let converted_back = ::PriceProvider::convert_back_to_normal_price( +// decimal_aware, +// USD_DECIMALS, +// asset_decimals, +// ) +// .unwrap(); +// assert_eq!(converted_back, original_price); +// +// // Test with an asset with more decimals than USD +// let original_price = FixedU128::from_float(1.85); +// let asset_decimals = 12; +// let decimal_aware = ::PriceProvider::calculate_decimals_aware_price( +// original_price, +// USD_DECIMALS, +// asset_decimals, +// ) +// .unwrap(); +// let converted_back = ::PriceProvider::convert_back_to_normal_price( +// decimal_aware, +// USD_DECIMALS, +// asset_decimals, +// ) +// .unwrap(); +// assert_eq!(converted_back, original_price); +// } +// +// #[test] +// fn calculate_evaluation_plmc_spent() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// const EVALUATOR_1: AccountIdOf = 1u32; +// const USD_AMOUNT_1: BalanceOf = 150_000 * USD_UNIT; +// const EXPECTED_PLMC_AMOUNT_1: f64 = 17_857.1428571428f64; +// +// const EVALUATOR_2: AccountIdOf = 2u32; +// const USD_AMOUNT_2: BalanceOf = 50_000 * USD_UNIT; +// const EXPECTED_PLMC_AMOUNT_2: f64 = 5_952.3809523809f64; +// +// const EVALUATOR_3: AccountIdOf = 3u32; +// const USD_AMOUNT_3: BalanceOf = 75_000 * USD_UNIT; +// const EXPECTED_PLMC_AMOUNT_3: f64 = 8_928.5714285714f64; +// +// const EVALUATOR_4: AccountIdOf = 4u32; +// const USD_AMOUNT_4: BalanceOf = 100 * USD_UNIT; +// const EXPECTED_PLMC_AMOUNT_4: f64 = 11.9047619047f64; +// +// const EVALUATOR_5: AccountIdOf = 5u32; +// +// // 123.7 USD +// const USD_AMOUNT_5: BalanceOf = 1237 * USD_UNIT / 10; +// const EXPECTED_PLMC_AMOUNT_5: f64 = 14.7261904761f64; +// +// const PLMC_PRICE: f64 = 8.4f64; +// +// assert_eq!( +// ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap(), +// PriceOf::::from_float(PLMC_PRICE) +// ); +// +// let evaluations = vec![ +// UserToUSDBalance::::new(EVALUATOR_1, USD_AMOUNT_1), +// UserToUSDBalance::::new(EVALUATOR_2, USD_AMOUNT_2), +// UserToUSDBalance::::new(EVALUATOR_3, USD_AMOUNT_3), +// UserToUSDBalance::::new(EVALUATOR_4, USD_AMOUNT_4), +// UserToUSDBalance::::new(EVALUATOR_5, USD_AMOUNT_5), +// ]; +// +// let expected_plmc_spent = vec![ +// (EVALUATOR_1, EXPECTED_PLMC_AMOUNT_1), +// (EVALUATOR_2, EXPECTED_PLMC_AMOUNT_2), +// (EVALUATOR_3, EXPECTED_PLMC_AMOUNT_3), +// (EVALUATOR_4, EXPECTED_PLMC_AMOUNT_4), +// (EVALUATOR_5, EXPECTED_PLMC_AMOUNT_5), +// ]; +// +// let calculated_plmc_spent = inst +// .calculate_evaluation_plmc_spent(evaluations, false) +// .into_iter() +// .sorted_by(|a, b| a.account.cmp(&b.account)) +// .map(|map| map.plmc_amount) +// .collect_vec(); +// let expected_plmc_spent = expected_plmc_spent +// .into_iter() +// .sorted_by(|a, b| a.0.cmp(&b.0)) +// .map(|map| { +// let f64_amount = map.1; +// let fixed_amount = FixedU128::from_float(f64_amount); +// fixed_amount.checked_mul_int(PLMC).unwrap() +// }) +// .collect_vec(); +// for (expected, calculated) in zip(expected_plmc_spent, calculated_plmc_spent) { +// assert_close_enough!(expected, calculated, Perquintill::from_float(0.999)); +// } +// } +// +// #[test] +// fn calculate_auction_plmc_returned() { +// const CT_AMOUNT_1: u128 = 5000 * CT_UNIT; +// const CT_AMOUNT_2: u128 = 40_000 * CT_UNIT; +// const CT_AMOUNT_3: u128 = 10_000 * CT_UNIT; +// const CT_AMOUNT_4: u128 = 6000 * CT_UNIT; +// const CT_AMOUNT_5: u128 = 2000 * CT_UNIT; +// +// let bid_1 = BidParams::new(BIDDER_1, CT_AMOUNT_1, 1u8, AcceptedFundingAsset::USDT); +// let bid_2 = BidParams::new(BIDDER_2, CT_AMOUNT_2, 1u8, AcceptedFundingAsset::USDT); +// let bid_3 = BidParams::new(BIDDER_1, CT_AMOUNT_3, 1u8, AcceptedFundingAsset::USDT); +// let bid_4 = BidParams::new(BIDDER_3, CT_AMOUNT_4, 1u8, AcceptedFundingAsset::USDT); +// let bid_5 = BidParams::new(BIDDER_4, CT_AMOUNT_5, 1u8, AcceptedFundingAsset::USDT); +// +// // post bucketing, the bids look like this: +// // (BIDDER_1, 5k) - (BIDDER_2, 40k) - (BIDDER_1, 5k) - (BIDDER_1, 5k) - (BIDDER_3 - 5k) - (BIDDER_3 - 1k) - (BIDDER_4 - 2k) +// // | -------------------- 1USD ----------------------|---- 1.1 USD ---|---- 1.2 USD ----|----------- 1.3 USD -------------| +// // post wap ~ 1.0557252: +// // (Accepted, 5k) - (Partially, 32k) - (Rejected, 5k) - (Accepted, 5k) - (Accepted - 5k) - (Accepted - 1k) - (Accepted - 2k) +// +// const ORIGINAL_PLMC_CHARGED_BIDDER_1: f64 = 18_452.3809523790; +// const ORIGINAL_PLMC_CHARGED_BIDDER_2: f64 = 47_619.0476190470; +// const ORIGINAL_PLMC_CHARGED_BIDDER_3: f64 = 86_90.4761904760; +// const ORIGINAL_PLMC_CHARGED_BIDDER_4: f64 = 30_95.2380952380; +// +// const FINAL_PLMC_CHARGED_BIDDER_1: f64 = 12_236.4594692840; +// const FINAL_PLMC_CHARGED_BIDDER_2: f64 = 38_095.2380952380; +// const FINAL_PLMC_CHARGED_BIDDER_3: f64 = 75_40.8942202840; +// const FINAL_PLMC_CHARGED_BIDDER_4: f64 = 2_513.6314067610; +// +// let bids = vec![bid_1, bid_2, bid_3, bid_4, bid_5]; +// +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = ProjectMetadata { +// token_information: default_token_information(), +// mainnet_token_max_supply: 8_000_000 * CT_UNIT, +// total_allocation_size: 100_000 * CT_UNIT, +// auction_round_allocation_percentage: Percent::from_percent(50u8), +// minimum_price: PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(10.0), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(), +// bidding_ticket_sizes: BiddingTicketSizes { +// professional: TicketSize::new(5000 * USD_UNIT, None), +// institutional: TicketSize::new(5000 * USD_UNIT, None), +// phantom: Default::default(), +// }, +// contributing_ticket_sizes: ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, None), +// professional: TicketSize::new(USD_UNIT, None), +// institutional: TicketSize::new(USD_UNIT, None), +// phantom: Default::default(), +// }, +// participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), +// funding_destination_account: ISSUER_1, +// policy_ipfs_cid: Some(ipfs_hash()), +// }; +// +// let project_id = inst.create_community_contributing_project( +// project_metadata.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// bids.clone(), +// ); +// +// let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); +// +// let expected_returns = vec![ +// ORIGINAL_PLMC_CHARGED_BIDDER_1 - FINAL_PLMC_CHARGED_BIDDER_1, +// ORIGINAL_PLMC_CHARGED_BIDDER_2 - FINAL_PLMC_CHARGED_BIDDER_2, +// ORIGINAL_PLMC_CHARGED_BIDDER_3 - FINAL_PLMC_CHARGED_BIDDER_3, +// ORIGINAL_PLMC_CHARGED_BIDDER_4 - FINAL_PLMC_CHARGED_BIDDER_4, +// ]; +// +// let mut returned_plmc_mappings = +// inst.calculate_auction_plmc_returned_from_all_bids_made(&bids, project_metadata.clone(), wap); +// returned_plmc_mappings.sort_by(|b1, b2| b1.account.cmp(&b2.account)); +// +// let returned_plmc_balances = returned_plmc_mappings.into_iter().map(|map| map.plmc_amount).collect_vec(); +// +// for (expected_return, returned_balance) in zip(expected_returns, returned_plmc_balances) { +// let expected_value = FixedU128::from_float(expected_return).checked_mul_int(PLMC).unwrap(); +// +// assert_close_enough!(expected_value, returned_balance, Perquintill::from_float(0.99)); +// } +// } +// +// #[test] +// fn bucket_wap_calculation() { +// let initial_price = FixedU128::from_float(10.0); +// let mut bucket = Bucket::new(100u32, initial_price, FixedU128::from_float(1.0), 10u32); +// let wap = bucket.calculate_wap(100u32); +// assert!(wap == initial_price); +// +// // Initial token amount: 100 +// // Simulate total bidding amount of 128 +// bucket.update(100u32); +// bucket.update(10u32); +// bucket.update(10u32); +// bucket.update(8u32); +// let wap = bucket.calculate_wap(100u32); +// let expected = FixedU128::from_float(10.628); +// let diff = if wap > expected { wap - expected } else { expected - wap }; +// assert!(diff <= FixedU128::from_float(0.001)); +// } +// +// #[test] +// fn calculate_contributed_plmc_spent() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// const PLMC_PRICE: f64 = 8.4f64; +// const CT_PRICE: f64 = 16.32f64; +// +// const CONTRIBUTOR_1: AccountIdOf = 1u32; +// const TOKEN_AMOUNT_1: u128 = 120 * CT_UNIT; +// const MULTIPLIER_1: u8 = 1u8; +// const _TICKET_SIZE_USD_1: u128 = 1_958_4_000_000_000_u128; +// const EXPECTED_PLMC_AMOUNT_1: f64 = 233.1_428_571_428f64; +// +// const CONTRIBUTOR_2: AccountIdOf = 2u32; +// const TOKEN_AMOUNT_2: u128 = 5023 * CT_UNIT; +// const MULTIPLIER_2: u8 = 2u8; +// const _TICKET_SIZE_USD_2: u128 = 81_975_3_600_000_000_u128; +// const EXPECTED_PLMC_AMOUNT_2: f64 = 4_879.4_857_142_857f64; +// +// const CONTRIBUTOR_3: AccountIdOf = 3u32; +// const TOKEN_AMOUNT_3: u128 = 20_000 * CT_UNIT; +// const MULTIPLIER_3: u8 = 17u8; +// const _TICKET_SIZE_USD_3: u128 = 326_400_0_000_000_000_u128; +// const EXPECTED_PLMC_AMOUNT_3: f64 = 2_285.7_142_857_142f64; +// +// const CONTRIBUTOR_4: AccountIdOf = 4u32; +// const TOKEN_AMOUNT_4: u128 = 1_000_000 * CT_UNIT; +// const MULTIPLIER_4: u8 = 25u8; +// const _TICKET_SIZE_4: u128 = 16_320_000_0_000_000_000_u128; +// const EXPECTED_PLMC_AMOUNT_4: f64 = 77_714.2_857_142_857f64; +// +// const CONTRIBUTOR_5: AccountIdOf = 5u32; +// // 0.1233 CTs +// const TOKEN_AMOUNT_5: u128 = 1_233 * CT_UNIT / 10_000; +// const MULTIPLIER_5: u8 = 10u8; +// const _TICKET_SIZE_5: u128 = 2_0_122_562_000_u128; +// const EXPECTED_PLMC_AMOUNT_5: f64 = 0.0_239_554_285f64; +// +// assert_eq!( +// ::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap(), +// PriceOf::::from_float(PLMC_PRICE) +// ); +// +// let contributions = vec![ +// ContributionParams::new(CONTRIBUTOR_1, TOKEN_AMOUNT_1, MULTIPLIER_1, AcceptedFundingAsset::USDT), +// ContributionParams::new(CONTRIBUTOR_2, TOKEN_AMOUNT_2, MULTIPLIER_2, AcceptedFundingAsset::USDT), +// ContributionParams::new(CONTRIBUTOR_3, TOKEN_AMOUNT_3, MULTIPLIER_3, AcceptedFundingAsset::USDT), +// ContributionParams::new(CONTRIBUTOR_4, TOKEN_AMOUNT_4, MULTIPLIER_4, AcceptedFundingAsset::USDT), +// ContributionParams::new(CONTRIBUTOR_5, TOKEN_AMOUNT_5, MULTIPLIER_5, AcceptedFundingAsset::USDT), +// ]; +// +// let expected_plmc_spent = vec![ +// (CONTRIBUTOR_1, EXPECTED_PLMC_AMOUNT_1), +// (CONTRIBUTOR_2, EXPECTED_PLMC_AMOUNT_2), +// (CONTRIBUTOR_3, EXPECTED_PLMC_AMOUNT_3), +// (CONTRIBUTOR_4, EXPECTED_PLMC_AMOUNT_4), +// (CONTRIBUTOR_5, EXPECTED_PLMC_AMOUNT_5), +// ]; +// +// let calculated_plmc_spent = inst +// .calculate_contributed_plmc_spent( +// contributions, +// PriceProviderOf::::calculate_decimals_aware_price( +// PriceOf::::from_float(CT_PRICE), +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(), +// false, +// ) +// .into_iter() +// .sorted_by(|a, b| a.account.cmp(&b.account)) +// .map(|map| map.plmc_amount) +// .collect_vec(); +// let expected_plmc_spent = expected_plmc_spent +// .into_iter() +// .sorted_by(|a, b| a.0.cmp(&b.0)) +// .map(|map| { +// let fixed_amount = FixedU128::from_float(map.1); +// fixed_amount.checked_mul_int(PLMC).unwrap() +// }) +// .collect_vec(); +// for (expected, calculated) in zip(expected_plmc_spent, calculated_plmc_spent) { +// assert_close_enough!(expected, calculated, Perquintill::from_float(0.999)); +// } +// } +// } +// +// // logic of small functions that extrinsics use to process data or interact with storage +// mod inner_functions { +// use super::*; +// +// #[test] +// fn calculate_vesting_duration() { +// let default_multiplier = MultiplierOf::::default(); +// let default_multiplier_duration = default_multiplier.calculate_vesting_duration::(); +// assert_eq!(default_multiplier_duration, 1u64); +// +// let multiplier_1 = MultiplierOf::::new(1u8).unwrap(); +// let multiplier_1_duration = multiplier_1.calculate_vesting_duration::(); +// assert_eq!(multiplier_1_duration, 1u64); +// +// let multiplier_2 = MultiplierOf::::new(2u8).unwrap(); +// let multiplier_2_duration = multiplier_2.calculate_vesting_duration::(); +// assert_eq!(multiplier_2_duration, FixedU128::from_rational(2167, 1000).saturating_mul_int((DAYS * 7) as u64)); +// +// let multiplier_3 = MultiplierOf::::new(3u8).unwrap(); +// let multiplier_3_duration = multiplier_3.calculate_vesting_duration::(); +// assert_eq!(multiplier_3_duration, FixedU128::from_rational(4334, 1000).saturating_mul_int((DAYS * 7) as u64)); +// +// let multiplier_19 = MultiplierOf::::new(19u8).unwrap(); +// let multiplier_19_duration = multiplier_19.calculate_vesting_duration::(); +// assert_eq!(multiplier_19_duration, FixedU128::from_rational(39006, 1000).saturating_mul_int((DAYS * 7) as u64)); +// +// let multiplier_20 = MultiplierOf::::new(20u8).unwrap(); +// let multiplier_20_duration = multiplier_20.calculate_vesting_duration::(); +// assert_eq!(multiplier_20_duration, FixedU128::from_rational(41173, 1000).saturating_mul_int((DAYS * 7) as u64)); +// +// let multiplier_24 = MultiplierOf::::new(24u8).unwrap(); +// let multiplier_24_duration = multiplier_24.calculate_vesting_duration::(); +// assert_eq!(multiplier_24_duration, FixedU128::from_rational(49841, 1000).saturating_mul_int((DAYS * 7) as u64)); +// +// let multiplier_25 = MultiplierOf::::new(25u8).unwrap(); +// let multiplier_25_duration = multiplier_25.calculate_vesting_duration::(); +// assert_eq!(multiplier_25_duration, FixedU128::from_rational(52008, 1000).saturating_mul_int((DAYS * 7) as u64)); +// } +// } diff --git a/pallets/funding/src/tests/mod.rs b/pallets/funding/src/tests/mod.rs index 43803e434..878578456 100644 --- a/pallets/funding/src/tests/mod.rs +++ b/pallets/funding/src/tests/mod.rs @@ -1,505 +1,505 @@ -use super::*; -use crate::{ - instantiator::*, - mock::*, - traits::{ProvideAssetPrice, VestingDurationCalculation}, - CurrencyMetadata, Error, ProjectMetadata, TicketSize, -}; -use defaults::*; -use frame_support::{ - assert_err, assert_noop, assert_ok, - traits::{ - fungible::{Inspect as FungibleInspect, InspectHold as FungibleInspectHold, MutateFreeze, MutateHold}, - Get, - }, -}; -use itertools::Itertools; -use parachains_common::DAYS; -use polimec_common::{ReleaseSchedule, USD_DECIMALS, USD_UNIT}; -use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt_with_cid}; -use sp_arithmetic::{traits::Zero, Percent, Perquintill}; -use sp_runtime::TokenError; -use sp_std::cell::RefCell; -use std::iter::zip; -pub type MockInstantiator = - Instantiator::AllPalletsWithoutSystem, RuntimeEvent>; -pub const CT_DECIMALS: u8 = 15; -pub const CT_UNIT: u128 = 10_u128.pow(CT_DECIMALS as u32); -pub const USDT_UNIT: u128 = USD_UNIT; - -const IPFS_CID: &str = "QmeuJ24ffwLAZppQcgcggJs3n689bewednYkuc8Bx5Gngz"; -const ISSUER_1: AccountId = 11; -const ISSUER_2: AccountId = 12; -const ISSUER_3: AccountId = 13; -const ISSUER_4: AccountId = 14; -const ISSUER_5: AccountId = 15; -const ISSUER_6: AccountId = 16; -const ISSUER_7: AccountId = 17; -const ISSUER_8: AccountId = 18; -const EVALUATOR_1: AccountId = 21; -const EVALUATOR_2: AccountId = 22; -const EVALUATOR_3: AccountId = 23; -const EVALUATOR_4: AccountId = 24; -const EVALUATOR_5: AccountId = 25; -const BIDDER_1: AccountId = 31; -const BIDDER_2: AccountId = 32; -const BIDDER_3: AccountId = 33; -const BIDDER_4: AccountId = 34; -const BIDDER_5: AccountId = 35; -const BIDDER_6: AccountId = 36; -const BUYER_1: AccountId = 41; -const BUYER_2: AccountId = 42; -const BUYER_3: AccountId = 43; -const BUYER_4: AccountId = 44; -const BUYER_5: AccountId = 45; -const BUYER_6: AccountId = 46; -const BUYER_7: AccountId = 47; -const BUYER_8: AccountId = 48; -const BUYER_9: AccountId = 49; - -#[path = "1_application.rs"] -mod application; -#[path = "3_auction.rs"] -mod auction; -#[path = "4_community.rs"] -mod community; -#[path = "8_ct_migration.rs"] -mod ct_migration; -#[path = "2_evaluation.rs"] -mod evaluation; -#[path = "6_funding_end.rs"] -mod funding_end; -mod misc; -#[path = "5_remainder.rs"] -mod remainder; -mod runtime_api; -#[path = "7_settlement.rs"] -mod settlement; - -pub mod defaults { - use super::*; - - pub fn default_token_information() -> CurrencyMetadata>> { - CurrencyMetadata { name: bounded_name(), symbol: bounded_symbol(), decimals: CT_DECIMALS } - } - pub fn default_project_metadata(issuer: AccountId) -> ProjectMetadataOf { - let bounded_name = bounded_name(); - let bounded_symbol = bounded_symbol(); - let metadata_hash = ipfs_hash(); - let base_price = PriceOf::::from_float(10.0); - let decimal_aware_price = ::PriceProvider::calculate_decimals_aware_price( - base_price, - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(); - ProjectMetadata { - token_information: CurrencyMetadata { name: bounded_name, symbol: bounded_symbol, decimals: CT_DECIMALS }, - mainnet_token_max_supply: 8_000_000 * CT_UNIT, - total_allocation_size: 1_000_000 * CT_UNIT, - auction_round_allocation_percentage: Percent::from_percent(50u8), - minimum_price: decimal_aware_price, - bidding_ticket_sizes: BiddingTicketSizes { - professional: TicketSize::new(5000 * USD_UNIT, None), - institutional: TicketSize::new(5000 * USD_UNIT, None), - phantom: Default::default(), - }, - contributing_ticket_sizes: ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, None), - professional: TicketSize::new(USD_UNIT, None), - institutional: TicketSize::new(USD_UNIT, None), - phantom: Default::default(), - }, - participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), - funding_destination_account: issuer, - policy_ipfs_cid: Some(metadata_hash), - } - } - - pub fn knowledge_hub_project() -> ProjectMetadataOf { - let bounded_name = bounded_name(); - let bounded_symbol = bounded_symbol(); - let metadata_hash = ipfs_hash(); - let base_price = PriceOf::::from_float(10.0); - let decimal_aware_price = ::PriceProvider::calculate_decimals_aware_price( - base_price, - USD_DECIMALS, - CT_DECIMALS, - ) - .unwrap(); - let project_metadata = ProjectMetadataOf:: { - token_information: CurrencyMetadata { name: bounded_name, symbol: bounded_symbol, decimals: CT_DECIMALS }, - mainnet_token_max_supply: 8_000_000 * CT_UNIT, - total_allocation_size: 100_000 * CT_UNIT, - auction_round_allocation_percentage: Percent::from_percent(50u8), - minimum_price: decimal_aware_price, - bidding_ticket_sizes: BiddingTicketSizes { - professional: TicketSize::new(5000 * USD_UNIT, None), - institutional: TicketSize::new(5000 * USD_UNIT, None), - phantom: Default::default(), - }, - contributing_ticket_sizes: ContributingTicketSizes { - retail: TicketSize::new(USD_UNIT, None), - professional: TicketSize::new(USD_UNIT, None), - institutional: TicketSize::new(USD_UNIT, None), - phantom: Default::default(), - }, - participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), - funding_destination_account: ISSUER_1, - policy_ipfs_cid: Some(metadata_hash), - }; - project_metadata - } - - pub fn default_plmc_balances() -> Vec> { - vec![ - UserToPLMCBalance::new(ISSUER_1, 10_000_000 * PLMC), - UserToPLMCBalance::new(EVALUATOR_1, 10_000_000 * PLMC), - UserToPLMCBalance::new(EVALUATOR_2, 10_000_000 * PLMC), - UserToPLMCBalance::new(EVALUATOR_3, 10_000_000 * PLMC), - UserToPLMCBalance::new(BIDDER_1, 10_000_000 * PLMC), - UserToPLMCBalance::new(BIDDER_2, 10_000_000 * PLMC), - UserToPLMCBalance::new(BUYER_1, 10_000_000 * PLMC), - UserToPLMCBalance::new(BUYER_2, 10_000_000 * PLMC), - UserToPLMCBalance::new(BUYER_3, 10_000_000 * PLMC), - UserToPLMCBalance::new(BUYER_4, 10_000_000 * PLMC), - UserToPLMCBalance::new(BUYER_5, 10_000_000 * PLMC), - ] - } - - pub fn default_usdt_balances() -> Vec> { - vec![ - (ISSUER_1, 10_000_000 * USDT_UNIT).into(), - (EVALUATOR_1, 10_000_000 * USDT_UNIT).into(), - (EVALUATOR_2, 10_000_000 * USDT_UNIT).into(), - (EVALUATOR_3, 10_000_000 * USDT_UNIT).into(), - (BIDDER_1, 10_000_000 * USDT_UNIT).into(), - (BIDDER_2, 10_000_000 * USDT_UNIT).into(), - (BUYER_1, 10_000_000 * USDT_UNIT).into(), - (BUYER_2, 10_000_000 * USDT_UNIT).into(), - (BUYER_3, 10_000_000 * USDT_UNIT).into(), - (BUYER_4, 10_000_000 * USDT_UNIT).into(), - (BUYER_5, 10_000_000 * USDT_UNIT).into(), - ] - } - - pub fn default_evaluations() -> Vec> { - vec![ - UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_3, 320_000 * USD_UNIT), - ] - } - - pub fn knowledge_hub_evaluations() -> Vec> { - vec![ - UserToUSDBalance::new(EVALUATOR_1, 75_000 * USDT_UNIT), - UserToUSDBalance::new(EVALUATOR_2, 65_000 * USDT_UNIT), - UserToUSDBalance::new(EVALUATOR_3, 60_000 * USDT_UNIT), - ] - } - - pub fn default_failing_evaluations() -> Vec> { - vec![UserToUSDBalance::new(EVALUATOR_1, 3_000 * USD_UNIT), UserToUSDBalance::new(EVALUATOR_2, 1_000 * USD_UNIT)] - } - - pub fn default_bids() -> Vec> { - vec![ - BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_2, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ] - } - - pub fn knowledge_hub_bids() -> Vec> { - // This should reflect the bidding currency, which currently is USDT - vec![ - BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_2, 20_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_3, 20_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_4, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_5, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_6, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ] - } - - pub fn default_community_buys() -> Vec> { - vec![ - ContributionParams::new(BUYER_1, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_2, 130_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_3, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_4, 210_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_5, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ] - } - - pub fn default_remainder_buys() -> Vec> { - vec![ - ContributionParams::new(EVALUATOR_2, 20_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_2, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BIDDER_1, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ] - } - - pub fn knowledge_hub_buys() -> Vec> { - vec![ - ContributionParams::new(BUYER_1, 4_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_2, 2_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_3, 2_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_4, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_5, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_6, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_7, 2_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ] - } - - pub fn bounded_name() -> BoundedVec> { - BoundedVec::try_from("Contribution Token TEST".as_bytes().to_vec()).unwrap() - } - pub fn bounded_symbol() -> BoundedVec> { - BoundedVec::try_from("CTEST".as_bytes().to_vec()).unwrap() - } - pub fn ipfs_hash() -> BoundedVec> { - BoundedVec::try_from(IPFS_CID.as_bytes().to_vec()).unwrap() - } - - pub fn default_weights() -> Vec { - vec![20u8, 15u8, 10u8, 25u8, 30u8] - } - - pub fn default_evaluators() -> Vec { - vec![EVALUATOR_1, EVALUATOR_2, EVALUATOR_3, EVALUATOR_4, EVALUATOR_5] - } - pub fn default_bidders() -> Vec { - vec![BIDDER_1, BIDDER_2, BIDDER_3, BIDDER_4, BIDDER_5] - } - pub fn default_multipliers() -> Vec { - vec![1u8, 1u8, 1u8, 1u8, 1u8] - } - pub fn default_bidder_multipliers() -> Vec { - vec![10u8, 3u8, 8u8, 1u8, 4u8] - } - pub fn default_community_contributor_multipliers() -> Vec { - vec![1u8, 1u8, 1u8, 1u8, 1u8] - } - pub fn default_remainder_contributor_multipliers() -> Vec { - vec![1u8, 1u8, 1u8, 1u8, 1u8] - } - - pub fn default_community_contributors() -> Vec { - vec![BUYER_1, BUYER_2, BUYER_3, BUYER_4, BUYER_5] - } - - pub fn default_remainder_contributors() -> Vec { - vec![EVALUATOR_1, BIDDER_3, BUYER_4, BUYER_6, BIDDER_6] - } - - pub fn default_all_participants() -> Vec { - let mut accounts: Vec = default_evaluators() - .iter() - .chain(default_bidders().iter()) - .chain(default_community_contributors().iter()) - .chain(default_remainder_contributors().iter()) - .copied() - .collect(); - accounts.sort(); - accounts.dedup(); - accounts - } - - pub fn project_from_funding_reached(instantiator: &mut MockInstantiator, percent: u64) -> ProjectId { - let project_metadata = default_project_metadata(ISSUER_1); - let min_price = project_metadata.minimum_price; - let usd_to_reach = Perquintill::from_percent(percent) * - (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); - let evaluations = default_evaluations(); - let bids = instantiator.generate_bids_from_total_usd( - Percent::from_percent(50u8) * usd_to_reach, - min_price, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let contributions = instantiator.generate_contributions_from_total_usd( - Percent::from_percent(50u8) * usd_to_reach, - min_price, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - instantiator.create_finished_project(project_metadata, ISSUER_1, None, evaluations, bids, contributions, vec![]) - } - - pub fn default_bids_from_ct_percent(percent: u8) -> Vec> { - // Used only to generate values, not for chain interactions - let inst = MockInstantiator::new(None); - let project_metadata = default_project_metadata(ISSUER_1); - inst.generate_bids_from_total_ct_percent( - project_metadata, - percent, - default_weights(), - default_bidders(), - default_bidder_multipliers(), - ) - } - - pub fn default_community_contributions_from_ct_percent(percent: u8) -> Vec> { - // Used only to generate values, not for chain interactions - let inst = MockInstantiator::new(None); - let project_metadata = default_project_metadata(ISSUER_1); - inst.generate_contributions_from_total_ct_percent( - project_metadata, - percent, - default_weights(), - default_community_contributors(), - default_community_contributor_multipliers(), - ) - } - - pub fn default_remainder_contributions_from_ct_percent(percent: u8) -> Vec> { - // Used only to generate values, not for chain interactions - let inst = MockInstantiator::new(None); - let project_metadata = default_project_metadata(ISSUER_1); - inst.generate_contributions_from_total_ct_percent( - project_metadata, - percent, - default_weights(), - default_remainder_contributors(), - default_remainder_contributor_multipliers(), - ) - } -} - -pub fn create_project_with_funding_percentage( - percentage: u64, - maybe_decision: Option, - start_settlement: bool, -) -> (MockInstantiator, ProjectId) { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_metadata = default_project_metadata(ISSUER_1); - let min_price = project_metadata.minimum_price; - let percentage_funded_usd = Perquintill::from_percent(percentage) * - (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); - let evaluations = default_evaluations(); - let bids = inst.generate_bids_from_total_usd( - Percent::from_percent(50u8) * percentage_funded_usd, - min_price, - default_weights(), - default_bidders(), - default_multipliers(), - ); - let contributions = inst.generate_contributions_from_total_usd( - Percent::from_percent(50u8) * percentage_funded_usd, - min_price, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let project_id = - inst.create_finished_project(project_metadata, ISSUER_1, None, evaluations, bids, contributions, vec![]); - - match inst.get_project_details(project_id).status { - ProjectStatus::FundingSuccessful => { - assert!(percentage >= 33); - }, - ProjectStatus::FundingFailed => { - assert!(percentage <= 33); - }, - _ => panic!("unexpected project status"), - }; - - if start_settlement { - let settlement_execution = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); - inst.jump_to_block(settlement_execution); - - let funding_sucessful = match percentage { - 0..=33 => false, - 34..=89 if matches!(maybe_decision, Some(FundingOutcomeDecision::RejectFunding)) => false, - 34..=89 if matches!(maybe_decision, Some(FundingOutcomeDecision::AcceptFunding)) => true, - 90..=100 => true, - _ => panic!("unexpected percentage"), - }; - if funding_sucessful { - assert_eq!( - inst.get_project_details(project_id).status, - ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful) - ); - inst.test_ct_created_for(project_id); - } else { - assert_eq!( - inst.get_project_details(project_id).status, - ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) - ); - inst.test_ct_not_created_for(project_id); - } - } - - (inst, project_id) -} - -pub fn create_finished_project_with_usd_raised( - mut inst: MockInstantiator, - usd_raised: BalanceOf, - usd_target: BalanceOf, -) -> (MockInstantiator, ProjectId) { - let issuer = inst.get_new_nonce() as u32; - let mut project_metadata = default_project_metadata(issuer); - project_metadata.total_allocation_size = - project_metadata.minimum_price.reciprocal().unwrap().saturating_mul_int(usd_target); - project_metadata.auction_round_allocation_percentage = Percent::from_percent(50u8); - - dbg!(project_metadata.minimum_price); - let required_price = if usd_raised <= usd_target { - project_metadata.minimum_price - } else { - // It's hard to know how much usd was raised on the auction to take the price to `x`. So we calculate - // the price needed to get the project from 0 to `usd_target` buying 50% of the supply in the contribution round. - // Later we adjust the exact amount of tokens based on the amount raised in the auction. - // This means we will never have 100% CTs sold. - let price_increase_percentage = FixedU128::from_rational(usd_raised, usd_target); - let required_price = price_increase_percentage * project_metadata.minimum_price; - - // Since we want to reach the usd target with half the tokens, and the usd target is first calculated based on - // selling all the CTs, we need the price to be double - FixedU128::from_rational(2, 1) * required_price - }; - dbg!(required_price); - - let evaluations = default_evaluations(); - - let bids = inst.generate_bids_that_take_price_to(project_metadata.clone(), required_price, 420, |acc| acc + 1u32); - - let project_id = inst.create_community_contributing_project(project_metadata, issuer, None, evaluations, bids); - - let project_details = inst.get_project_details(project_id); - let wap = project_details.weighted_average_price.unwrap(); - dbg!(wap); - - let usd_raised_so_far = project_details.funding_amount_reached_usd; - let usd_remaining = usd_raised - usd_raised_so_far; - - let community_contributions = inst.generate_contributions_from_total_usd( - usd_remaining, - wap, - default_weights(), - default_community_contributors(), - default_multipliers(), - ); - let plmc_required = inst.calculate_contributed_plmc_spent(community_contributions.clone(), required_price, true); - let usdt_required = inst.calculate_contributed_funding_asset_spent(community_contributions.clone(), required_price); - inst.mint_plmc_to(plmc_required); - inst.mint_foreign_asset_to(usdt_required); - inst.contribute_for_users(project_id, community_contributions).unwrap(); - inst.start_remainder_or_end_funding(project_id).unwrap(); - if matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(_)) { - inst.finish_funding(project_id, Some(FundingOutcomeDecision::AcceptFunding)).unwrap(); - } - - let project_details = inst.get_project_details(project_id); - dbg!(project_details.remaining_contribution_tokens); - assert_eq!(project_details.status, ProjectStatus::FundingSuccessful); - // We are happy if the amount raised is 99.999 of what we wanted - assert_close_enough!(project_details.funding_amount_reached_usd, usd_raised, Perquintill::from_float(0.999)); - assert_eq!(project_details.fundraising_target_usd, usd_target); - - (inst, project_id) -} +// use super::*; +// use crate::{ +// instantiator::*, +// mock::*, +// traits::{ProvideAssetPrice, VestingDurationCalculation}, +// CurrencyMetadata, Error, ProjectMetadata, TicketSize, +// }; +// use defaults::*; +// use frame_support::{ +// assert_err, assert_noop, assert_ok, +// traits::{ +// fungible::{Inspect as FungibleInspect, InspectHold as FungibleInspectHold, MutateFreeze, MutateHold}, +// Get, +// }, +// }; +// use itertools::Itertools; +// use parachains_common::DAYS; +// use polimec_common::{ReleaseSchedule, USD_DECIMALS, USD_UNIT}; +// use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt_with_cid}; +// use sp_arithmetic::{traits::Zero, Percent, Perquintill}; +// use sp_runtime::TokenError; +// use sp_std::cell::RefCell; +// use std::iter::zip; +// pub type MockInstantiator = +// Instantiator::AllPalletsWithoutSystem, RuntimeEvent>; +// pub const CT_DECIMALS: u8 = 15; +// pub const CT_UNIT: u128 = 10_u128.pow(CT_DECIMALS as u32); +// pub const USDT_UNIT: u128 = USD_UNIT; +// +// const IPFS_CID: &str = "QmeuJ24ffwLAZppQcgcggJs3n689bewednYkuc8Bx5Gngz"; +// const ISSUER_1: AccountId = 11; +// const ISSUER_2: AccountId = 12; +// const ISSUER_3: AccountId = 13; +// const ISSUER_4: AccountId = 14; +// const ISSUER_5: AccountId = 15; +// const ISSUER_6: AccountId = 16; +// const ISSUER_7: AccountId = 17; +// const ISSUER_8: AccountId = 18; +// const EVALUATOR_1: AccountId = 21; +// const EVALUATOR_2: AccountId = 22; +// const EVALUATOR_3: AccountId = 23; +// const EVALUATOR_4: AccountId = 24; +// const EVALUATOR_5: AccountId = 25; +// const BIDDER_1: AccountId = 31; +// const BIDDER_2: AccountId = 32; +// const BIDDER_3: AccountId = 33; +// const BIDDER_4: AccountId = 34; +// const BIDDER_5: AccountId = 35; +// const BIDDER_6: AccountId = 36; +// const BUYER_1: AccountId = 41; +// const BUYER_2: AccountId = 42; +// const BUYER_3: AccountId = 43; +// const BUYER_4: AccountId = 44; +// const BUYER_5: AccountId = 45; +// const BUYER_6: AccountId = 46; +// const BUYER_7: AccountId = 47; +// const BUYER_8: AccountId = 48; +// const BUYER_9: AccountId = 49; +// +// #[path = "1_application.rs"] +// mod application; +// #[path = "3_auction.rs"] +// mod auction; +// #[path = "4_community.rs"] +// mod community; +// #[path = "8_ct_migration.rs"] +// mod ct_migration; +// #[path = "2_evaluation.rs"] +// mod evaluation; +// #[path = "6_funding_end.rs"] +// mod funding_end; +// mod misc; +// #[path = "5_remainder.rs"] +// mod remainder; +// mod runtime_api; +// #[path = "7_settlement.rs"] +// mod settlement; +// +// pub mod defaults { +// use super::*; +// +// pub fn default_token_information() -> CurrencyMetadata>> { +// CurrencyMetadata { name: bounded_name(), symbol: bounded_symbol(), decimals: CT_DECIMALS } +// } +// pub fn default_project_metadata(issuer: AccountId) -> ProjectMetadataOf { +// let bounded_name = bounded_name(); +// let bounded_symbol = bounded_symbol(); +// let metadata_hash = ipfs_hash(); +// let base_price = PriceOf::::from_float(10.0); +// let decimal_aware_price = ::PriceProvider::calculate_decimals_aware_price( +// base_price, +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(); +// ProjectMetadata { +// token_information: CurrencyMetadata { name: bounded_name, symbol: bounded_symbol, decimals: CT_DECIMALS }, +// mainnet_token_max_supply: 8_000_000 * CT_UNIT, +// total_allocation_size: 1_000_000 * CT_UNIT, +// auction_round_allocation_percentage: Percent::from_percent(50u8), +// minimum_price: decimal_aware_price, +// bidding_ticket_sizes: BiddingTicketSizes { +// professional: TicketSize::new(5000 * USD_UNIT, None), +// institutional: TicketSize::new(5000 * USD_UNIT, None), +// phantom: Default::default(), +// }, +// contributing_ticket_sizes: ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, None), +// professional: TicketSize::new(USD_UNIT, None), +// institutional: TicketSize::new(USD_UNIT, None), +// phantom: Default::default(), +// }, +// participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), +// funding_destination_account: issuer, +// policy_ipfs_cid: Some(metadata_hash), +// } +// } +// +// pub fn knowledge_hub_project() -> ProjectMetadataOf { +// let bounded_name = bounded_name(); +// let bounded_symbol = bounded_symbol(); +// let metadata_hash = ipfs_hash(); +// let base_price = PriceOf::::from_float(10.0); +// let decimal_aware_price = ::PriceProvider::calculate_decimals_aware_price( +// base_price, +// USD_DECIMALS, +// CT_DECIMALS, +// ) +// .unwrap(); +// let project_metadata = ProjectMetadataOf:: { +// token_information: CurrencyMetadata { name: bounded_name, symbol: bounded_symbol, decimals: CT_DECIMALS }, +// mainnet_token_max_supply: 8_000_000 * CT_UNIT, +// total_allocation_size: 100_000 * CT_UNIT, +// auction_round_allocation_percentage: Percent::from_percent(50u8), +// minimum_price: decimal_aware_price, +// bidding_ticket_sizes: BiddingTicketSizes { +// professional: TicketSize::new(5000 * USD_UNIT, None), +// institutional: TicketSize::new(5000 * USD_UNIT, None), +// phantom: Default::default(), +// }, +// contributing_ticket_sizes: ContributingTicketSizes { +// retail: TicketSize::new(USD_UNIT, None), +// professional: TicketSize::new(USD_UNIT, None), +// institutional: TicketSize::new(USD_UNIT, None), +// phantom: Default::default(), +// }, +// participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(), +// funding_destination_account: ISSUER_1, +// policy_ipfs_cid: Some(metadata_hash), +// }; +// project_metadata +// } +// +// pub fn default_plmc_balances() -> Vec> { +// vec![ +// UserToPLMCBalance::new(ISSUER_1, 10_000_000 * PLMC), +// UserToPLMCBalance::new(EVALUATOR_1, 10_000_000 * PLMC), +// UserToPLMCBalance::new(EVALUATOR_2, 10_000_000 * PLMC), +// UserToPLMCBalance::new(EVALUATOR_3, 10_000_000 * PLMC), +// UserToPLMCBalance::new(BIDDER_1, 10_000_000 * PLMC), +// UserToPLMCBalance::new(BIDDER_2, 10_000_000 * PLMC), +// UserToPLMCBalance::new(BUYER_1, 10_000_000 * PLMC), +// UserToPLMCBalance::new(BUYER_2, 10_000_000 * PLMC), +// UserToPLMCBalance::new(BUYER_3, 10_000_000 * PLMC), +// UserToPLMCBalance::new(BUYER_4, 10_000_000 * PLMC), +// UserToPLMCBalance::new(BUYER_5, 10_000_000 * PLMC), +// ] +// } +// +// pub fn default_usdt_balances() -> Vec> { +// vec![ +// (ISSUER_1, 10_000_000 * USDT_UNIT).into(), +// (EVALUATOR_1, 10_000_000 * USDT_UNIT).into(), +// (EVALUATOR_2, 10_000_000 * USDT_UNIT).into(), +// (EVALUATOR_3, 10_000_000 * USDT_UNIT).into(), +// (BIDDER_1, 10_000_000 * USDT_UNIT).into(), +// (BIDDER_2, 10_000_000 * USDT_UNIT).into(), +// (BUYER_1, 10_000_000 * USDT_UNIT).into(), +// (BUYER_2, 10_000_000 * USDT_UNIT).into(), +// (BUYER_3, 10_000_000 * USDT_UNIT).into(), +// (BUYER_4, 10_000_000 * USDT_UNIT).into(), +// (BUYER_5, 10_000_000 * USDT_UNIT).into(), +// ] +// } +// +// pub fn default_evaluations() -> Vec> { +// vec![ +// UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_3, 320_000 * USD_UNIT), +// ] +// } +// +// pub fn knowledge_hub_evaluations() -> Vec> { +// vec![ +// UserToUSDBalance::new(EVALUATOR_1, 75_000 * USDT_UNIT), +// UserToUSDBalance::new(EVALUATOR_2, 65_000 * USDT_UNIT), +// UserToUSDBalance::new(EVALUATOR_3, 60_000 * USDT_UNIT), +// ] +// } +// +// pub fn default_failing_evaluations() -> Vec> { +// vec![UserToUSDBalance::new(EVALUATOR_1, 3_000 * USD_UNIT), UserToUSDBalance::new(EVALUATOR_2, 1_000 * USD_UNIT)] +// } +// +// pub fn default_bids() -> Vec> { +// vec![ +// BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_2, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ] +// } +// +// pub fn knowledge_hub_bids() -> Vec> { +// // This should reflect the bidding currency, which currently is USDT +// vec![ +// BidParams::new(BIDDER_1, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_2, 20_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_3, 20_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_4, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_5, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_6, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ] +// } +// +// pub fn default_community_buys() -> Vec> { +// vec![ +// ContributionParams::new(BUYER_1, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_2, 130_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_3, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_4, 210_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_5, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ] +// } +// +// pub fn default_remainder_buys() -> Vec> { +// vec![ +// ContributionParams::new(EVALUATOR_2, 20_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_2, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BIDDER_1, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ] +// } +// +// pub fn knowledge_hub_buys() -> Vec> { +// vec![ +// ContributionParams::new(BUYER_1, 4_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_2, 2_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_3, 2_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_4, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_5, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_6, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_7, 2_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ] +// } +// +// pub fn bounded_name() -> BoundedVec> { +// BoundedVec::try_from("Contribution Token TEST".as_bytes().to_vec()).unwrap() +// } +// pub fn bounded_symbol() -> BoundedVec> { +// BoundedVec::try_from("CTEST".as_bytes().to_vec()).unwrap() +// } +// pub fn ipfs_hash() -> BoundedVec> { +// BoundedVec::try_from(IPFS_CID.as_bytes().to_vec()).unwrap() +// } +// +// pub fn default_weights() -> Vec { +// vec![20u8, 15u8, 10u8, 25u8, 30u8] +// } +// +// pub fn default_evaluators() -> Vec { +// vec![EVALUATOR_1, EVALUATOR_2, EVALUATOR_3, EVALUATOR_4, EVALUATOR_5] +// } +// pub fn default_bidders() -> Vec { +// vec![BIDDER_1, BIDDER_2, BIDDER_3, BIDDER_4, BIDDER_5] +// } +// pub fn default_multipliers() -> Vec { +// vec![1u8, 1u8, 1u8, 1u8, 1u8] +// } +// pub fn default_bidder_multipliers() -> Vec { +// vec![10u8, 3u8, 8u8, 1u8, 4u8] +// } +// pub fn default_community_contributor_multipliers() -> Vec { +// vec![1u8, 1u8, 1u8, 1u8, 1u8] +// } +// pub fn default_remainder_contributor_multipliers() -> Vec { +// vec![1u8, 1u8, 1u8, 1u8, 1u8] +// } +// +// pub fn default_community_contributors() -> Vec { +// vec![BUYER_1, BUYER_2, BUYER_3, BUYER_4, BUYER_5] +// } +// +// pub fn default_remainder_contributors() -> Vec { +// vec![EVALUATOR_1, BIDDER_3, BUYER_4, BUYER_6, BIDDER_6] +// } +// +// pub fn default_all_participants() -> Vec { +// let mut accounts: Vec = default_evaluators() +// .iter() +// .chain(default_bidders().iter()) +// .chain(default_community_contributors().iter()) +// .chain(default_remainder_contributors().iter()) +// .copied() +// .collect(); +// accounts.sort(); +// accounts.dedup(); +// accounts +// } +// +// pub fn project_from_funding_reached(instantiator: &mut MockInstantiator, percent: u64) -> ProjectId { +// let project_metadata = default_project_metadata(ISSUER_1); +// let min_price = project_metadata.minimum_price; +// let usd_to_reach = Perquintill::from_percent(percent) * +// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); +// let evaluations = default_evaluations(); +// let bids = instantiator.generate_bids_from_total_usd( +// Percent::from_percent(50u8) * usd_to_reach, +// min_price, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let contributions = instantiator.generate_contributions_from_total_usd( +// Percent::from_percent(50u8) * usd_to_reach, +// min_price, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// instantiator.create_finished_project(project_metadata, ISSUER_1, None, evaluations, bids, contributions, vec![]) +// } +// +// pub fn default_bids_from_ct_percent(percent: u8) -> Vec> { +// // Used only to generate values, not for chain interactions +// let inst = MockInstantiator::new(None); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.generate_bids_from_total_ct_percent( +// project_metadata, +// percent, +// default_weights(), +// default_bidders(), +// default_bidder_multipliers(), +// ) +// } +// +// pub fn default_community_contributions_from_ct_percent(percent: u8) -> Vec> { +// // Used only to generate values, not for chain interactions +// let inst = MockInstantiator::new(None); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.generate_contributions_from_total_ct_percent( +// project_metadata, +// percent, +// default_weights(), +// default_community_contributors(), +// default_community_contributor_multipliers(), +// ) +// } +// +// pub fn default_remainder_contributions_from_ct_percent(percent: u8) -> Vec> { +// // Used only to generate values, not for chain interactions +// let inst = MockInstantiator::new(None); +// let project_metadata = default_project_metadata(ISSUER_1); +// inst.generate_contributions_from_total_ct_percent( +// project_metadata, +// percent, +// default_weights(), +// default_remainder_contributors(), +// default_remainder_contributor_multipliers(), +// ) +// } +// } +// +// pub fn create_project_with_funding_percentage( +// percentage: u64, +// maybe_decision: Option, +// start_settlement: bool, +// ) -> (MockInstantiator, ProjectId) { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_metadata = default_project_metadata(ISSUER_1); +// let min_price = project_metadata.minimum_price; +// let percentage_funded_usd = Perquintill::from_percent(percentage) * +// (project_metadata.minimum_price.checked_mul_int(project_metadata.total_allocation_size).unwrap()); +// let evaluations = default_evaluations(); +// let bids = inst.generate_bids_from_total_usd( +// Percent::from_percent(50u8) * percentage_funded_usd, +// min_price, +// default_weights(), +// default_bidders(), +// default_multipliers(), +// ); +// let contributions = inst.generate_contributions_from_total_usd( +// Percent::from_percent(50u8) * percentage_funded_usd, +// min_price, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let project_id = +// inst.create_finished_project(project_metadata, ISSUER_1, None, evaluations, bids, contributions, vec![]); +// +// match inst.get_project_details(project_id).status { +// ProjectStatus::FundingSuccessful => { +// assert!(percentage >= 33); +// }, +// ProjectStatus::FundingFailed => { +// assert!(percentage <= 33); +// }, +// _ => panic!("unexpected project status"), +// }; +// +// if start_settlement { +// let settlement_execution = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap(); +// inst.jump_to_block(settlement_execution); +// +// let funding_sucessful = match percentage { +// 0..=33 => false, +// 34..=89 if matches!(maybe_decision, Some(FundingOutcomeDecision::RejectFunding)) => false, +// 34..=89 if matches!(maybe_decision, Some(FundingOutcomeDecision::AcceptFunding)) => true, +// 90..=100 => true, +// _ => panic!("unexpected percentage"), +// }; +// if funding_sucessful { +// assert_eq!( +// inst.get_project_details(project_id).status, +// ProjectStatus::SettlementStarted(FundingOutcome::FundingSuccessful) +// ); +// inst.test_ct_created_for(project_id); +// } else { +// assert_eq!( +// inst.get_project_details(project_id).status, +// ProjectStatus::SettlementStarted(FundingOutcome::FundingFailed) +// ); +// inst.test_ct_not_created_for(project_id); +// } +// } +// +// (inst, project_id) +// } +// +// pub fn create_finished_project_with_usd_raised( +// mut inst: MockInstantiator, +// usd_raised: BalanceOf, +// usd_target: BalanceOf, +// ) -> (MockInstantiator, ProjectId) { +// let issuer = inst.get_new_nonce() as u32; +// let mut project_metadata = default_project_metadata(issuer); +// project_metadata.total_allocation_size = +// project_metadata.minimum_price.reciprocal().unwrap().saturating_mul_int(usd_target); +// project_metadata.auction_round_allocation_percentage = Percent::from_percent(50u8); +// +// dbg!(project_metadata.minimum_price); +// let required_price = if usd_raised <= usd_target { +// project_metadata.minimum_price +// } else { +// // It's hard to know how much usd was raised on the auction to take the price to `x`. So we calculate +// // the price needed to get the project from 0 to `usd_target` buying 50% of the supply in the contribution round. +// // Later we adjust the exact amount of tokens based on the amount raised in the auction. +// // This means we will never have 100% CTs sold. +// let price_increase_percentage = FixedU128::from_rational(usd_raised, usd_target); +// let required_price = price_increase_percentage * project_metadata.minimum_price; +// +// // Since we want to reach the usd target with half the tokens, and the usd target is first calculated based on +// // selling all the CTs, we need the price to be double +// FixedU128::from_rational(2, 1) * required_price +// }; +// dbg!(required_price); +// +// let evaluations = default_evaluations(); +// +// let bids = inst.generate_bids_that_take_price_to(project_metadata.clone(), required_price, 420, |acc| acc + 1u32); +// +// let project_id = inst.create_community_contributing_project(project_metadata, issuer, None, evaluations, bids); +// +// let project_details = inst.get_project_details(project_id); +// let wap = project_details.weighted_average_price.unwrap(); +// dbg!(wap); +// +// let usd_raised_so_far = project_details.funding_amount_reached_usd; +// let usd_remaining = usd_raised - usd_raised_so_far; +// +// let community_contributions = inst.generate_contributions_from_total_usd( +// usd_remaining, +// wap, +// default_weights(), +// default_community_contributors(), +// default_multipliers(), +// ); +// let plmc_required = inst.calculate_contributed_plmc_spent(community_contributions.clone(), required_price, true); +// let usdt_required = inst.calculate_contributed_funding_asset_spent(community_contributions.clone(), required_price); +// inst.mint_plmc_to(plmc_required); +// inst.mint_foreign_asset_to(usdt_required); +// inst.contribute_for_users(project_id, community_contributions).unwrap(); +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// if matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(_)) { +// inst.finish_funding(project_id, Some(FundingOutcomeDecision::AcceptFunding)).unwrap(); +// } +// +// let project_details = inst.get_project_details(project_id); +// dbg!(project_details.remaining_contribution_tokens); +// assert_eq!(project_details.status, ProjectStatus::FundingSuccessful); +// // We are happy if the amount raised is 99.999 of what we wanted +// assert_close_enough!(project_details.funding_amount_reached_usd, usd_raised, Perquintill::from_float(0.999)); +// assert_eq!(project_details.fundraising_target_usd, usd_target); +// +// (inst, project_id) +// } diff --git a/pallets/funding/src/tests/runtime_api.rs b/pallets/funding/src/tests/runtime_api.rs index 66001f2e9..dad0c7e04 100644 --- a/pallets/funding/src/tests/runtime_api.rs +++ b/pallets/funding/src/tests/runtime_api.rs @@ -1,695 +1,695 @@ -use super::*; -use crate::runtime_api::{ExtrinsicHelpers, Leaderboards, ProjectInformation, UserInformation}; - -#[test] -fn top_evaluations() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let evaluations = vec![ - UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_3, 320_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_4, 1_000_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_1, 1_000 * USD_UNIT), - ]; - let project_id = inst.create_auctioning_project(default_project_metadata(ISSUER_1), ISSUER_1, None, evaluations); - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let top_1 = TestRuntime::top_evaluations(&TestRuntime, block_hash, project_id, 1).unwrap(); - let evaluator_4_evaluation = Evaluations::::get((project_id, EVALUATOR_4, 3)).unwrap(); - assert!(top_1.len() == 1 && top_1[0] == evaluator_4_evaluation); - - let top_4_evaluators = TestRuntime::top_evaluations(&TestRuntime, block_hash, project_id, 4) - .unwrap() - .into_iter() - .map(|evaluation| evaluation.evaluator) - .collect_vec(); - assert_eq!(top_4_evaluators, vec![EVALUATOR_4, EVALUATOR_1, EVALUATOR_3, EVALUATOR_2]); - - let top_6_evaluators = TestRuntime::top_evaluations(&TestRuntime, block_hash, project_id, 6) - .unwrap() - .into_iter() - .map(|evaluation| evaluation.evaluator) - .collect_vec(); - assert_eq!(top_6_evaluators, vec![EVALUATOR_4, EVALUATOR_1, EVALUATOR_3, EVALUATOR_2, EVALUATOR_1]); - }); -} - -#[test] -fn top_bids() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let bids = vec![ - (BIDDER_1, 8000 * CT_UNIT).into(), - (BIDDER_2, 501 * CT_UNIT).into(), - (BIDDER_3, 1200 * CT_UNIT).into(), - (BIDDER_4, 10400 * CT_UNIT).into(), - (BIDDER_1, 500 * CT_UNIT).into(), - ]; - let project_id = inst.create_community_contributing_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - bids, - ); - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let top_1 = TestRuntime::top_bids(&TestRuntime, block_hash, project_id, 1).unwrap(); - let bidder_4_evaluation = Bids::::get((project_id, BIDDER_4, 3)).unwrap(); - assert!(top_1.len() == 1 && top_1[0] == bidder_4_evaluation); - - let top_4_bidders = TestRuntime::top_bids(&TestRuntime, block_hash, project_id, 4) - .unwrap() - .into_iter() - .map(|evaluation| evaluation.bidder) - .collect_vec(); - assert_eq!(top_4_bidders, vec![BIDDER_4, BIDDER_1, BIDDER_3, BIDDER_2]); - - let top_6_bidders = TestRuntime::top_bids(&TestRuntime, block_hash, project_id, 6) - .unwrap() - .into_iter() - .map(|evaluation| evaluation.bidder) - .collect_vec(); - assert_eq!(top_6_bidders, vec![BIDDER_4, BIDDER_1, BIDDER_3, BIDDER_2, BIDDER_1]); - }); -} - -#[test] -fn top_contributions() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let community_contributors = - vec![(BUYER_1, 8000 * CT_UNIT).into(), (BUYER_2, 501 * CT_UNIT).into(), (BUYER_3, 1200 * CT_UNIT).into()]; - let remainder_contributors = vec![(BUYER_4, 10400 * CT_UNIT).into(), (BUYER_1, 500 * CT_UNIT).into()]; - let project_id = inst.create_finished_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - community_contributors, - remainder_contributors, - ); - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let top_1 = TestRuntime::top_contributions(&TestRuntime, block_hash, project_id, 1).unwrap(); - let contributor_4_evaluation = Contributions::::get((project_id, BUYER_4, 3)).unwrap(); - assert!(top_1.len() == 1 && top_1[0] == contributor_4_evaluation); - - let top_4_contributors = TestRuntime::top_contributions(&TestRuntime, block_hash, project_id, 4) - .unwrap() - .into_iter() - .map(|evaluation| evaluation.contributor) - .collect_vec(); - assert_eq!(top_4_contributors, vec![BUYER_4, BUYER_1, BUYER_3, BUYER_2]); - - let top_6_contributors = TestRuntime::top_contributions(&TestRuntime, block_hash, project_id, 6) - .unwrap() - .into_iter() - .map(|evaluation| evaluation.contributor) - .collect_vec(); - assert_eq!(top_6_contributors, vec![BUYER_4, BUYER_1, BUYER_3, BUYER_2, BUYER_1]); - }); -} - -#[test] -fn top_projects_by_usd_raised() { - let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let (inst, project_id_1) = create_finished_project_with_usd_raised(inst, 400_000 * USD_UNIT, 1_000_000 * USD_UNIT); - let (inst, project_id_2) = - create_finished_project_with_usd_raised(inst, 1_200_000 * USD_UNIT, 1_000_000 * USD_UNIT); - let (inst, project_id_3) = - create_finished_project_with_usd_raised(inst, 3_000_000 * USD_UNIT, 1_000_000 * USD_UNIT); - let (inst, project_id_4) = create_finished_project_with_usd_raised(inst, 840_000 * USD_UNIT, 1_000_000 * USD_UNIT); - let (mut inst, project_id_5) = - create_finished_project_with_usd_raised(inst, 980_000 * USD_UNIT, 1_000_000 * USD_UNIT); - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let top_1 = TestRuntime::top_projects_by_usd_raised(&TestRuntime, block_hash, 1u32).unwrap(); - let project_3_details = ProjectsDetails::::get(project_id_3).unwrap(); - let project_3_metadata = ProjectsMetadata::::get(project_id_3).unwrap(); - assert!(top_1.len() == 1 && top_1[0] == (project_id_3, project_3_metadata, project_3_details)); - - let top_4 = TestRuntime::top_projects_by_usd_raised(&TestRuntime, block_hash, 4u32) - .unwrap() - .into_iter() - .map(|(project_id, project_metadata, project_details)| { - let stored_metadata = ProjectsMetadata::::get(project_id).unwrap(); - let stored_details = ProjectsDetails::::get(project_id).unwrap(); - assert!(project_metadata == stored_metadata && project_details == stored_details); - project_id - }) - .collect_vec(); - - assert_eq!(top_4, vec![project_id_3, project_id_2, project_id_5, project_id_4]); - - let top_6 = TestRuntime::top_projects_by_usd_raised(&TestRuntime, block_hash, 6u32) - .unwrap() - .into_iter() - .map(|(project_id, project_metadata, project_details)| { - let stored_metadata = ProjectsMetadata::::get(project_id).unwrap(); - let stored_details = ProjectsDetails::::get(project_id).unwrap(); - assert!(project_metadata == stored_metadata && project_details == stored_details); - project_id - }) - .collect_vec(); - - assert_eq!(top_6, vec![project_id_3, project_id_2, project_id_5, project_id_4, project_id_1]); - }); -} - -#[test] -fn top_projects_by_usd_target_percent_reached() { - let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let (inst, project_id_1) = - create_finished_project_with_usd_raised(inst, 2_000_000 * USD_UNIT, 1_000_000 * USD_UNIT); - let (inst, project_id_2) = create_finished_project_with_usd_raised(inst, 945_000 * USD_UNIT, 1_000_000 * USD_UNIT); - let (inst, project_id_3) = create_finished_project_with_usd_raised(inst, 500_000 * USD_UNIT, 100_000 * USD_UNIT); - - let (mut inst, project_id_4) = create_finished_project_with_usd_raised(inst, 50_000 * USD_UNIT, 100_000 * USD_UNIT); - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let top_1 = TestRuntime::top_projects_by_usd_target_percent_reached(&TestRuntime, block_hash, 1u32).unwrap(); - let project_3_details = ProjectsDetails::::get(project_id_3).unwrap(); - let project_3_metadata = ProjectsMetadata::::get(project_id_3).unwrap(); - assert!(top_1.len() == 1 && top_1[0] == (project_id_3, project_3_metadata, project_3_details)); - - let top_3 = TestRuntime::top_projects_by_usd_target_percent_reached(&TestRuntime, block_hash, 3u32) - .unwrap() - .into_iter() - .map(|(project_id, project_metadata, project_details)| { - let stored_metadata = ProjectsMetadata::::get(project_id).unwrap(); - let stored_details = ProjectsDetails::::get(project_id).unwrap(); - assert!(project_metadata == stored_metadata && project_details == stored_details); - project_id - }) - .collect_vec(); - - assert_eq!(top_3, vec![project_id_3, project_id_1, project_id_2]); - - let top_6 = TestRuntime::top_projects_by_usd_target_percent_reached(&TestRuntime, block_hash, 6u32) - .unwrap() - .into_iter() - .map(|(project_id, project_metadata, project_details)| { - let stored_metadata = ProjectsMetadata::::get(project_id).unwrap(); - let stored_details = ProjectsDetails::::get(project_id).unwrap(); - assert!(project_metadata == stored_metadata && project_details == stored_details); - project_id - }) - .collect_vec(); - - assert_eq!(top_6, vec![project_id_3, project_id_1, project_id_2, project_id_4]); - }); -} - -#[test] -fn contribution_tokens() { - let bob = 420; - let mut contributions_with_bob_1 = default_community_buys(); - let bob_amount_1 = 10_000 * CT_UNIT; - contributions_with_bob_1.last_mut().unwrap().contributor = bob; - contributions_with_bob_1.last_mut().unwrap().amount = bob_amount_1; - - let mut contributions_with_bob_2 = default_community_buys(); - let bob_amount_2 = 25_000 * CT_UNIT; - contributions_with_bob_2.last_mut().unwrap().contributor = bob; - contributions_with_bob_2.last_mut().unwrap().amount = bob_amount_2; - - let mut contributions_with_bob_3 = default_community_buys(); - let bob_amount_3 = 5_020 * CT_UNIT; - contributions_with_bob_3.last_mut().unwrap().contributor = bob; - contributions_with_bob_3.last_mut().unwrap().amount = bob_amount_3; - - let mut contributions_with_bob_4 = default_community_buys(); - let bob_amount_4 = 420 * CT_UNIT; - contributions_with_bob_4.last_mut().unwrap().contributor = bob; - contributions_with_bob_4.last_mut().unwrap().amount = bob_amount_4; - - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id_1 = inst.create_settled_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - contributions_with_bob_1, - default_remainder_buys(), - ); - let _project_id_2 = inst.create_settled_project( - default_project_metadata(ISSUER_2), - ISSUER_2, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - let _project_id_3 = inst.create_settled_project( - default_project_metadata(ISSUER_3), - ISSUER_3, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - let project_id_4 = inst.create_settled_project( - default_project_metadata(ISSUER_4), - ISSUER_4, - None, - default_evaluations(), - default_bids(), - contributions_with_bob_2, - default_remainder_buys(), - ); - let _project_id_5 = inst.create_settled_project( - default_project_metadata(ISSUER_5), - ISSUER_5, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - let project_id_6 = inst.create_settled_project( - default_project_metadata(ISSUER_6), - ISSUER_6, - None, - default_evaluations(), - default_bids(), - contributions_with_bob_3, - default_remainder_buys(), - ); - let project_id_7 = inst.create_settled_project( - default_project_metadata(ISSUER_7), - ISSUER_7, - None, - default_evaluations(), - default_bids(), - contributions_with_bob_4, - default_remainder_buys(), - ); - - let expected_items = vec![ - (project_id_4, bob_amount_2), - (project_id_1, bob_amount_1), - (project_id_6, bob_amount_3), - (project_id_7, bob_amount_4), - ]; - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let bob_items = TestRuntime::contribution_tokens(&TestRuntime, block_hash, bob.clone()).unwrap(); - assert_eq!(bob_items, expected_items); - }); -} - -#[test] -fn funding_asset_to_ct_amount() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - // We want to use a funding asset that is not equal to 1 USD - // Sanity check - assert_eq!( - PriceProviderOf::::get_price(AcceptedFundingAsset::DOT.to_assethub_id()).unwrap(), - PriceOf::::from_float(69.0f64) - ); - - let dot_amount: u128 = 1350_0_000_000_000; - // USD Ticket = 93_150 USD - - // Easy case, wap is already calculated, we want to know how many tokens at wap we can buy with `x` USDT - let project_metadata_1 = default_project_metadata(ISSUER_1); - let project_id_1 = inst.create_community_contributing_project( - project_metadata_1.clone(), - ISSUER_1, - None, - default_evaluations(), - vec![], - ); - let wap = project_metadata_1.minimum_price; - assert_eq!(inst.get_project_details(project_id_1).weighted_average_price.unwrap(), wap); - - // Price of ct is min price = 10 USD/CT - let expected_ct_amount_contribution = 9_315 * CT_UNIT; - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let ct_amount = TestRuntime::funding_asset_to_ct_amount( - &TestRuntime, - block_hash, - project_id_1, - AcceptedFundingAsset::DOT, - dot_amount, - ) - .unwrap(); - assert_eq!(ct_amount, expected_ct_amount_contribution); - }); - - // Medium case, contribution at a wap that is not the minimum price. - let project_metadata_2 = default_project_metadata(ISSUER_2); - let new_price = PriceOf::::from_float(16.3f64); - let decimal_aware_price = - PriceProviderOf::::calculate_decimals_aware_price(new_price, USD_DECIMALS, CT_DECIMALS).unwrap(); - - let bids = - inst.generate_bids_that_take_price_to(project_metadata_2.clone(), decimal_aware_price, 420u32, |acc| acc + 1); - let project_id_2 = inst.create_community_contributing_project( - project_metadata_2.clone(), - ISSUER_2, - None, - default_evaluations(), - bids, - ); - // Sanity check - let project_details = inst.get_project_details(project_id_2); - assert_eq!(project_details.weighted_average_price.unwrap(), decimal_aware_price); - - // 5'714.72... rounded down - let expected_ct_amount_contribution = 5_714_720_000_000_000_000; - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let ct_amount = TestRuntime::funding_asset_to_ct_amount( - &TestRuntime, - block_hash, - project_id_2, - AcceptedFundingAsset::DOT, - dot_amount, - ) - .unwrap(); - assert_close_enough!(ct_amount, expected_ct_amount_contribution, Perquintill::from_float(0.999f64)); - }); - - // Medium case, a bid goes over part of a bucket (bucket after the first one) - let project_metadata_3 = default_project_metadata(ISSUER_3); - let project_id_3 = - inst.create_auctioning_project(project_metadata_3.clone(), ISSUER_3, None, default_evaluations()); - let mut bucket = inst.execute(|| Buckets::::get(project_id_3)).unwrap(); - - // We want a full bucket after filling 6 buckets. (first bucket has full allocation and initial price) - // Price should be at 16 USD/CT - bucket.current_price = bucket.initial_price + bucket.delta_price * FixedU128::from_float(6.0f64); - bucket.amount_left = bucket.delta_amount; - let bids = inst.generate_bids_from_bucket( - project_metadata_3.clone(), - bucket, - 420u32, - |acc| acc + 1, - AcceptedFundingAsset::USDT, - ); - let necessary_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata_3.clone(), - None, - true, - ); - let necessary_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata_3.clone(), - None, - ); - inst.mint_plmc_to(necessary_plmc); - inst.mint_foreign_asset_to(necessary_usdt); - inst.bid_for_users(project_id_3, bids).unwrap(); - - // Sanity check - let expected_price = PriceOf::::from_float(16.0f64); - let decimal_aware_expected_price = - PriceProviderOf::::calculate_decimals_aware_price(expected_price, USD_DECIMALS, CT_DECIMALS) - .unwrap(); - let current_bucket = inst.execute(|| Buckets::::get(project_id_3).unwrap()); - assert_eq!(current_bucket.current_price, decimal_aware_expected_price); - - dbg!(current_bucket.current_price.saturating_mul_int(current_bucket.amount_left)); - - let dot_amount: u128 = 217_0_000_000_000; - let expected_ct_amount: u128 = 935_812_500_000_000_000; - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let ct_amount = TestRuntime::funding_asset_to_ct_amount( - &TestRuntime, - block_hash, - project_id_3, - AcceptedFundingAsset::DOT, - dot_amount, - ) - .unwrap(); - assert_eq!(ct_amount, expected_ct_amount); - }); - - // Hard case, a bid goes over multiple buckets - // We take the same project from before, and we add a bid that goes over 3 buckets. - // Bucket size is 50k CTs, and current price is 16 USD/CT - // We need to buy 50k at 16 , 50k at 17, and 13.5k at 18 = 1893k USD - - // Amount needed to spend 1893k USD through several buckets with DOT at 69 USD/DOT - let dot_amount = 27_434_7_826_086_956u128; - let expected_ct_amount = 113_500 * CT_UNIT; - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let ct_amount = TestRuntime::funding_asset_to_ct_amount( - &TestRuntime, - block_hash, - project_id_3, - AcceptedFundingAsset::DOT, - dot_amount, - ) - .unwrap(); - assert_close_enough!(ct_amount, expected_ct_amount, Perquintill::from_float(0.9999)); - }); -} - -#[test] -fn all_project_participations_by_did() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - - let did_user = generate_did_from_account(420); - let project_metadata = default_project_metadata(ISSUER_1); - let cid = project_metadata.clone().policy_ipfs_cid.unwrap(); - let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - - let evaluations = vec![ - UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT), - UserToUSDBalance::new(EVALUATOR_3, 320_000 * USD_UNIT), - ]; - let bids = vec![ - BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - BidParams::new(BIDDER_2, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ]; - let community_contributions = vec![ - ContributionParams::new(BUYER_1, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_2, 130_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_3, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_4, 210_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_5, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ]; - let remainder_contributions = vec![ - ContributionParams::new(EVALUATOR_2, 20_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BUYER_2, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ContributionParams::new(BIDDER_1, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), - ]; - - let evaluations_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); - let bids_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - true, - ); - let community_contributions_plmc = inst.calculate_contributed_plmc_spent( - community_contributions.clone(), - project_metadata.minimum_price.clone(), - true, - ); - let remainder_contributions_plmc = inst.calculate_contributed_plmc_spent( - remainder_contributions.clone(), - project_metadata.minimum_price.clone(), - true, - ); - let all_plmc = inst.generic_map_operation( - vec![evaluations_plmc, bids_plmc, community_contributions_plmc, remainder_contributions_plmc], - MergeOperation::Add, - ); - inst.mint_plmc_to(all_plmc); - - let bids_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( - &bids, - project_metadata.clone(), - None, - ); - let community_contributions_usdt = inst.calculate_contributed_funding_asset_spent( - community_contributions.clone(), - project_metadata.minimum_price.clone(), - ); - let remainder_contributions_usdt = inst.calculate_contributed_funding_asset_spent( - remainder_contributions.clone(), - project_metadata.minimum_price.clone(), - ); - let all_usdt = inst.generic_map_operation( - vec![bids_usdt, community_contributions_usdt, remainder_contributions_usdt], - MergeOperation::Add, - ); - inst.mint_foreign_asset_to(all_usdt); - - inst.evaluate_for_users(project_id, evaluations[..1].to_vec()).unwrap(); - for evaluation in evaluations[1..].to_vec() { - let jwt = get_mock_jwt_with_cid(evaluation.account, InvestorType::Retail, did_user.clone(), cid.clone()); - inst.execute(|| { - PolimecFunding::evaluate(RuntimeOrigin::signed(evaluation.account), jwt, project_id, evaluation.usd_amount) - .unwrap(); - }); - } - - inst.start_auction(project_id, ISSUER_1).unwrap(); - - inst.bid_for_users(project_id, bids[..1].to_vec()).unwrap(); - for bid in bids[1..].to_vec() { - let jwt = get_mock_jwt_with_cid(bid.bidder, InvestorType::Institutional, did_user.clone(), cid.clone()); - inst.execute(|| { - PolimecFunding::bid( - RuntimeOrigin::signed(bid.bidder), - jwt, - project_id, - bid.amount, - bid.multiplier, - bid.asset, - ) - .unwrap(); - }); - } - - inst.start_community_funding(project_id).unwrap(); - - inst.contribute_for_users(project_id, community_contributions).unwrap(); - - inst.start_remainder_or_end_funding(project_id).unwrap(); - - for contribution in remainder_contributions { - let jwt = - get_mock_jwt_with_cid(contribution.contributor, InvestorType::Professional, did_user.clone(), cid.clone()); - inst.execute(|| { - PolimecFunding::contribute( - RuntimeOrigin::signed(contribution.contributor), - jwt, - project_id, - contribution.amount, - contribution.multiplier, - contribution.asset, - ) - .unwrap(); - }); - } - - inst.finish_funding(project_id, None).unwrap(); - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let items = - TestRuntime::all_project_participations_by_did(&TestRuntime, block_hash, project_id, did_user).unwrap(); - dbg!(items); - }); -} - -#[test] -fn usd_target_percent_reached() { - let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let (inst, project_id_1) = - create_finished_project_with_usd_raised(inst, 2_000_000 * USD_UNIT, 1_000_000 * USD_UNIT); - let (inst, project_id_2) = create_finished_project_with_usd_raised(inst, 945_000 * USD_UNIT, 1_000_000 * USD_UNIT); - let (inst, project_id_3) = create_finished_project_with_usd_raised(inst, 517_000 * USD_UNIT, 100_000 * USD_UNIT); - - let (mut inst, project_id_4) = create_finished_project_with_usd_raised(inst, 50_000 * USD_UNIT, 100_000 * USD_UNIT); - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let percent_200: FixedU128 = - TestRuntime::usd_target_percent_reached(&TestRuntime, block_hash, project_id_1).unwrap(); - assert_close_enough!( - percent_200.into_inner(), - FixedU128::from_float(2.0f64).into_inner(), - Perquintill::from_float(0.999) - ); - - let percent_94_5: FixedU128 = - TestRuntime::usd_target_percent_reached(&TestRuntime, block_hash, project_id_2).unwrap(); - assert_close_enough!( - percent_94_5.into_inner(), - FixedU128::from_float(0.945f64).into_inner(), - Perquintill::from_float(0.999) - ); - - let percent_517: FixedU128 = - TestRuntime::usd_target_percent_reached(&TestRuntime, block_hash, project_id_3).unwrap(); - assert_close_enough!( - percent_517.into_inner(), - FixedU128::from_float(5.17f64).into_inner(), - Perquintill::from_float(0.999) - ); - - let percent_50: FixedU128 = - TestRuntime::usd_target_percent_reached(&TestRuntime, block_hash, project_id_4).unwrap(); - assert_close_enough!( - percent_50.into_inner(), - FixedU128::from_float(0.5f64).into_inner(), - Perquintill::from_float(0.999) - ); - }); -} - -#[test] -fn projects_by_did() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let did_user = generate_did_from_account(420); - - let project_id_1 = inst.create_settled_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - Some(did_user.clone()), - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - - let _project_id_2 = inst.create_settled_project( - default_project_metadata(ISSUER_1), - ISSUER_1, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - - let project_id_3 = inst.create_settled_project( - default_project_metadata(ISSUER_2), - ISSUER_2, - Some(did_user.clone()), - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - - let _project_id_4 = inst.create_settled_project( - default_project_metadata(ISSUER_3), - ISSUER_3, - None, - default_evaluations(), - default_bids(), - default_community_buys(), - default_remainder_buys(), - ); - - inst.execute(|| { - let block_hash = System::block_hash(System::block_number()); - let project_ids = TestRuntime::projects_by_did(&TestRuntime, block_hash, did_user).unwrap(); - assert_eq!(project_ids, vec![project_id_1, project_id_3]); - }); -} +// use super::*; +// use crate::runtime_api::{ExtrinsicHelpers, Leaderboards, ProjectInformation, UserInformation}; +// +// #[test] +// fn top_evaluations() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let evaluations = vec![ +// UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_3, 320_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_4, 1_000_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_1, 1_000 * USD_UNIT), +// ]; +// let project_id = inst.create_auctioning_project(default_project_metadata(ISSUER_1), ISSUER_1, None, evaluations); +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let top_1 = TestRuntime::top_evaluations(&TestRuntime, block_hash, project_id, 1).unwrap(); +// let evaluator_4_evaluation = Evaluations::::get((project_id, EVALUATOR_4, 3)).unwrap(); +// assert!(top_1.len() == 1 && top_1[0] == evaluator_4_evaluation); +// +// let top_4_evaluators = TestRuntime::top_evaluations(&TestRuntime, block_hash, project_id, 4) +// .unwrap() +// .into_iter() +// .map(|evaluation| evaluation.evaluator) +// .collect_vec(); +// assert_eq!(top_4_evaluators, vec![EVALUATOR_4, EVALUATOR_1, EVALUATOR_3, EVALUATOR_2]); +// +// let top_6_evaluators = TestRuntime::top_evaluations(&TestRuntime, block_hash, project_id, 6) +// .unwrap() +// .into_iter() +// .map(|evaluation| evaluation.evaluator) +// .collect_vec(); +// assert_eq!(top_6_evaluators, vec![EVALUATOR_4, EVALUATOR_1, EVALUATOR_3, EVALUATOR_2, EVALUATOR_1]); +// }); +// } +// +// #[test] +// fn top_bids() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let bids = vec![ +// (BIDDER_1, 8000 * CT_UNIT).into(), +// (BIDDER_2, 501 * CT_UNIT).into(), +// (BIDDER_3, 1200 * CT_UNIT).into(), +// (BIDDER_4, 10400 * CT_UNIT).into(), +// (BIDDER_1, 500 * CT_UNIT).into(), +// ]; +// let project_id = inst.create_community_contributing_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// bids, +// ); +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let top_1 = TestRuntime::top_bids(&TestRuntime, block_hash, project_id, 1).unwrap(); +// let bidder_4_evaluation = Bids::::get((project_id, BIDDER_4, 3)).unwrap(); +// assert!(top_1.len() == 1 && top_1[0] == bidder_4_evaluation); +// +// let top_4_bidders = TestRuntime::top_bids(&TestRuntime, block_hash, project_id, 4) +// .unwrap() +// .into_iter() +// .map(|evaluation| evaluation.bidder) +// .collect_vec(); +// assert_eq!(top_4_bidders, vec![BIDDER_4, BIDDER_1, BIDDER_3, BIDDER_2]); +// +// let top_6_bidders = TestRuntime::top_bids(&TestRuntime, block_hash, project_id, 6) +// .unwrap() +// .into_iter() +// .map(|evaluation| evaluation.bidder) +// .collect_vec(); +// assert_eq!(top_6_bidders, vec![BIDDER_4, BIDDER_1, BIDDER_3, BIDDER_2, BIDDER_1]); +// }); +// } +// +// #[test] +// fn top_contributions() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let community_contributors = +// vec![(BUYER_1, 8000 * CT_UNIT).into(), (BUYER_2, 501 * CT_UNIT).into(), (BUYER_3, 1200 * CT_UNIT).into()]; +// let remainder_contributors = vec![(BUYER_4, 10400 * CT_UNIT).into(), (BUYER_1, 500 * CT_UNIT).into()]; +// let project_id = inst.create_finished_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// community_contributors, +// remainder_contributors, +// ); +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let top_1 = TestRuntime::top_contributions(&TestRuntime, block_hash, project_id, 1).unwrap(); +// let contributor_4_evaluation = Contributions::::get((project_id, BUYER_4, 3)).unwrap(); +// assert!(top_1.len() == 1 && top_1[0] == contributor_4_evaluation); +// +// let top_4_contributors = TestRuntime::top_contributions(&TestRuntime, block_hash, project_id, 4) +// .unwrap() +// .into_iter() +// .map(|evaluation| evaluation.contributor) +// .collect_vec(); +// assert_eq!(top_4_contributors, vec![BUYER_4, BUYER_1, BUYER_3, BUYER_2]); +// +// let top_6_contributors = TestRuntime::top_contributions(&TestRuntime, block_hash, project_id, 6) +// .unwrap() +// .into_iter() +// .map(|evaluation| evaluation.contributor) +// .collect_vec(); +// assert_eq!(top_6_contributors, vec![BUYER_4, BUYER_1, BUYER_3, BUYER_2, BUYER_1]); +// }); +// } +// +// #[test] +// fn top_projects_by_usd_raised() { +// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let (inst, project_id_1) = create_finished_project_with_usd_raised(inst, 400_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// let (inst, project_id_2) = +// create_finished_project_with_usd_raised(inst, 1_200_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// let (inst, project_id_3) = +// create_finished_project_with_usd_raised(inst, 3_000_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// let (inst, project_id_4) = create_finished_project_with_usd_raised(inst, 840_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// let (mut inst, project_id_5) = +// create_finished_project_with_usd_raised(inst, 980_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let top_1 = TestRuntime::top_projects_by_usd_raised(&TestRuntime, block_hash, 1u32).unwrap(); +// let project_3_details = ProjectsDetails::::get(project_id_3).unwrap(); +// let project_3_metadata = ProjectsMetadata::::get(project_id_3).unwrap(); +// assert!(top_1.len() == 1 && top_1[0] == (project_id_3, project_3_metadata, project_3_details)); +// +// let top_4 = TestRuntime::top_projects_by_usd_raised(&TestRuntime, block_hash, 4u32) +// .unwrap() +// .into_iter() +// .map(|(project_id, project_metadata, project_details)| { +// let stored_metadata = ProjectsMetadata::::get(project_id).unwrap(); +// let stored_details = ProjectsDetails::::get(project_id).unwrap(); +// assert!(project_metadata == stored_metadata && project_details == stored_details); +// project_id +// }) +// .collect_vec(); +// +// assert_eq!(top_4, vec![project_id_3, project_id_2, project_id_5, project_id_4]); +// +// let top_6 = TestRuntime::top_projects_by_usd_raised(&TestRuntime, block_hash, 6u32) +// .unwrap() +// .into_iter() +// .map(|(project_id, project_metadata, project_details)| { +// let stored_metadata = ProjectsMetadata::::get(project_id).unwrap(); +// let stored_details = ProjectsDetails::::get(project_id).unwrap(); +// assert!(project_metadata == stored_metadata && project_details == stored_details); +// project_id +// }) +// .collect_vec(); +// +// assert_eq!(top_6, vec![project_id_3, project_id_2, project_id_5, project_id_4, project_id_1]); +// }); +// } +// +// #[test] +// fn top_projects_by_usd_target_percent_reached() { +// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let (inst, project_id_1) = +// create_finished_project_with_usd_raised(inst, 2_000_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// let (inst, project_id_2) = create_finished_project_with_usd_raised(inst, 945_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// let (inst, project_id_3) = create_finished_project_with_usd_raised(inst, 500_000 * USD_UNIT, 100_000 * USD_UNIT); +// +// let (mut inst, project_id_4) = create_finished_project_with_usd_raised(inst, 50_000 * USD_UNIT, 100_000 * USD_UNIT); +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let top_1 = TestRuntime::top_projects_by_usd_target_percent_reached(&TestRuntime, block_hash, 1u32).unwrap(); +// let project_3_details = ProjectsDetails::::get(project_id_3).unwrap(); +// let project_3_metadata = ProjectsMetadata::::get(project_id_3).unwrap(); +// assert!(top_1.len() == 1 && top_1[0] == (project_id_3, project_3_metadata, project_3_details)); +// +// let top_3 = TestRuntime::top_projects_by_usd_target_percent_reached(&TestRuntime, block_hash, 3u32) +// .unwrap() +// .into_iter() +// .map(|(project_id, project_metadata, project_details)| { +// let stored_metadata = ProjectsMetadata::::get(project_id).unwrap(); +// let stored_details = ProjectsDetails::::get(project_id).unwrap(); +// assert!(project_metadata == stored_metadata && project_details == stored_details); +// project_id +// }) +// .collect_vec(); +// +// assert_eq!(top_3, vec![project_id_3, project_id_1, project_id_2]); +// +// let top_6 = TestRuntime::top_projects_by_usd_target_percent_reached(&TestRuntime, block_hash, 6u32) +// .unwrap() +// .into_iter() +// .map(|(project_id, project_metadata, project_details)| { +// let stored_metadata = ProjectsMetadata::::get(project_id).unwrap(); +// let stored_details = ProjectsDetails::::get(project_id).unwrap(); +// assert!(project_metadata == stored_metadata && project_details == stored_details); +// project_id +// }) +// .collect_vec(); +// +// assert_eq!(top_6, vec![project_id_3, project_id_1, project_id_2, project_id_4]); +// }); +// } +// +// #[test] +// fn contribution_tokens() { +// let bob = 420; +// let mut contributions_with_bob_1 = default_community_buys(); +// let bob_amount_1 = 10_000 * CT_UNIT; +// contributions_with_bob_1.last_mut().unwrap().contributor = bob; +// contributions_with_bob_1.last_mut().unwrap().amount = bob_amount_1; +// +// let mut contributions_with_bob_2 = default_community_buys(); +// let bob_amount_2 = 25_000 * CT_UNIT; +// contributions_with_bob_2.last_mut().unwrap().contributor = bob; +// contributions_with_bob_2.last_mut().unwrap().amount = bob_amount_2; +// +// let mut contributions_with_bob_3 = default_community_buys(); +// let bob_amount_3 = 5_020 * CT_UNIT; +// contributions_with_bob_3.last_mut().unwrap().contributor = bob; +// contributions_with_bob_3.last_mut().unwrap().amount = bob_amount_3; +// +// let mut contributions_with_bob_4 = default_community_buys(); +// let bob_amount_4 = 420 * CT_UNIT; +// contributions_with_bob_4.last_mut().unwrap().contributor = bob; +// contributions_with_bob_4.last_mut().unwrap().amount = bob_amount_4; +// +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let project_id_1 = inst.create_settled_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// contributions_with_bob_1, +// default_remainder_buys(), +// ); +// let _project_id_2 = inst.create_settled_project( +// default_project_metadata(ISSUER_2), +// ISSUER_2, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// let _project_id_3 = inst.create_settled_project( +// default_project_metadata(ISSUER_3), +// ISSUER_3, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// let project_id_4 = inst.create_settled_project( +// default_project_metadata(ISSUER_4), +// ISSUER_4, +// None, +// default_evaluations(), +// default_bids(), +// contributions_with_bob_2, +// default_remainder_buys(), +// ); +// let _project_id_5 = inst.create_settled_project( +// default_project_metadata(ISSUER_5), +// ISSUER_5, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// let project_id_6 = inst.create_settled_project( +// default_project_metadata(ISSUER_6), +// ISSUER_6, +// None, +// default_evaluations(), +// default_bids(), +// contributions_with_bob_3, +// default_remainder_buys(), +// ); +// let project_id_7 = inst.create_settled_project( +// default_project_metadata(ISSUER_7), +// ISSUER_7, +// None, +// default_evaluations(), +// default_bids(), +// contributions_with_bob_4, +// default_remainder_buys(), +// ); +// +// let expected_items = vec![ +// (project_id_4, bob_amount_2), +// (project_id_1, bob_amount_1), +// (project_id_6, bob_amount_3), +// (project_id_7, bob_amount_4), +// ]; +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let bob_items = TestRuntime::contribution_tokens(&TestRuntime, block_hash, bob.clone()).unwrap(); +// assert_eq!(bob_items, expected_items); +// }); +// } +// +// #[test] +// fn funding_asset_to_ct_amount() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// // We want to use a funding asset that is not equal to 1 USD +// // Sanity check +// assert_eq!( +// PriceProviderOf::::get_price(AcceptedFundingAsset::DOT.to_assethub_id()).unwrap(), +// PriceOf::::from_float(69.0f64) +// ); +// +// let dot_amount: u128 = 1350_0_000_000_000; +// // USD Ticket = 93_150 USD +// +// // Easy case, wap is already calculated, we want to know how many tokens at wap we can buy with `x` USDT +// let project_metadata_1 = default_project_metadata(ISSUER_1); +// let project_id_1 = inst.create_community_contributing_project( +// project_metadata_1.clone(), +// ISSUER_1, +// None, +// default_evaluations(), +// vec![], +// ); +// let wap = project_metadata_1.minimum_price; +// assert_eq!(inst.get_project_details(project_id_1).weighted_average_price.unwrap(), wap); +// +// // Price of ct is min price = 10 USD/CT +// let expected_ct_amount_contribution = 9_315 * CT_UNIT; +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let ct_amount = TestRuntime::funding_asset_to_ct_amount( +// &TestRuntime, +// block_hash, +// project_id_1, +// AcceptedFundingAsset::DOT, +// dot_amount, +// ) +// .unwrap(); +// assert_eq!(ct_amount, expected_ct_amount_contribution); +// }); +// +// // Medium case, contribution at a wap that is not the minimum price. +// let project_metadata_2 = default_project_metadata(ISSUER_2); +// let new_price = PriceOf::::from_float(16.3f64); +// let decimal_aware_price = +// PriceProviderOf::::calculate_decimals_aware_price(new_price, USD_DECIMALS, CT_DECIMALS).unwrap(); +// +// let bids = +// inst.generate_bids_that_take_price_to(project_metadata_2.clone(), decimal_aware_price, 420u32, |acc| acc + 1); +// let project_id_2 = inst.create_community_contributing_project( +// project_metadata_2.clone(), +// ISSUER_2, +// None, +// default_evaluations(), +// bids, +// ); +// // Sanity check +// let project_details = inst.get_project_details(project_id_2); +// assert_eq!(project_details.weighted_average_price.unwrap(), decimal_aware_price); +// +// // 5'714.72... rounded down +// let expected_ct_amount_contribution = 5_714_720_000_000_000_000; +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let ct_amount = TestRuntime::funding_asset_to_ct_amount( +// &TestRuntime, +// block_hash, +// project_id_2, +// AcceptedFundingAsset::DOT, +// dot_amount, +// ) +// .unwrap(); +// assert_close_enough!(ct_amount, expected_ct_amount_contribution, Perquintill::from_float(0.999f64)); +// }); +// +// // Medium case, a bid goes over part of a bucket (bucket after the first one) +// let project_metadata_3 = default_project_metadata(ISSUER_3); +// let project_id_3 = +// inst.create_auctioning_project(project_metadata_3.clone(), ISSUER_3, None, default_evaluations()); +// let mut bucket = inst.execute(|| Buckets::::get(project_id_3)).unwrap(); +// +// // We want a full bucket after filling 6 buckets. (first bucket has full allocation and initial price) +// // Price should be at 16 USD/CT +// bucket.current_price = bucket.initial_price + bucket.delta_price * FixedU128::from_float(6.0f64); +// bucket.amount_left = bucket.delta_amount; +// let bids = inst.generate_bids_from_bucket( +// project_metadata_3.clone(), +// bucket, +// 420u32, +// |acc| acc + 1, +// AcceptedFundingAsset::USDT, +// ); +// let necessary_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata_3.clone(), +// None, +// true, +// ); +// let necessary_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata_3.clone(), +// None, +// ); +// inst.mint_plmc_to(necessary_plmc); +// inst.mint_foreign_asset_to(necessary_usdt); +// inst.bid_for_users(project_id_3, bids).unwrap(); +// +// // Sanity check +// let expected_price = PriceOf::::from_float(16.0f64); +// let decimal_aware_expected_price = +// PriceProviderOf::::calculate_decimals_aware_price(expected_price, USD_DECIMALS, CT_DECIMALS) +// .unwrap(); +// let current_bucket = inst.execute(|| Buckets::::get(project_id_3).unwrap()); +// assert_eq!(current_bucket.current_price, decimal_aware_expected_price); +// +// dbg!(current_bucket.current_price.saturating_mul_int(current_bucket.amount_left)); +// +// let dot_amount: u128 = 217_0_000_000_000; +// let expected_ct_amount: u128 = 935_812_500_000_000_000; +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let ct_amount = TestRuntime::funding_asset_to_ct_amount( +// &TestRuntime, +// block_hash, +// project_id_3, +// AcceptedFundingAsset::DOT, +// dot_amount, +// ) +// .unwrap(); +// assert_eq!(ct_amount, expected_ct_amount); +// }); +// +// // Hard case, a bid goes over multiple buckets +// // We take the same project from before, and we add a bid that goes over 3 buckets. +// // Bucket size is 50k CTs, and current price is 16 USD/CT +// // We need to buy 50k at 16 , 50k at 17, and 13.5k at 18 = 1893k USD +// +// // Amount needed to spend 1893k USD through several buckets with DOT at 69 USD/DOT +// let dot_amount = 27_434_7_826_086_956u128; +// let expected_ct_amount = 113_500 * CT_UNIT; +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let ct_amount = TestRuntime::funding_asset_to_ct_amount( +// &TestRuntime, +// block_hash, +// project_id_3, +// AcceptedFundingAsset::DOT, +// dot_amount, +// ) +// .unwrap(); +// assert_close_enough!(ct_amount, expected_ct_amount, Perquintill::from_float(0.9999)); +// }); +// } +// +// #[test] +// fn all_project_participations_by_did() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// +// let did_user = generate_did_from_account(420); +// let project_metadata = default_project_metadata(ISSUER_1); +// let cid = project_metadata.clone().policy_ipfs_cid.unwrap(); +// let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); +// +// let evaluations = vec![ +// UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT), +// UserToUSDBalance::new(EVALUATOR_3, 320_000 * USD_UNIT), +// ]; +// let bids = vec![ +// BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// BidParams::new(BIDDER_2, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ]; +// let community_contributions = vec![ +// ContributionParams::new(BUYER_1, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_2, 130_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_3, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_4, 210_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_5, 10_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ]; +// let remainder_contributions = vec![ +// ContributionParams::new(EVALUATOR_2, 20_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BUYER_2, 5_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ContributionParams::new(BIDDER_1, 30_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT), +// ]; +// +// let evaluations_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone(), true); +// let bids_plmc = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata.clone(), +// None, +// true, +// ); +// let community_contributions_plmc = inst.calculate_contributed_plmc_spent( +// community_contributions.clone(), +// project_metadata.minimum_price.clone(), +// true, +// ); +// let remainder_contributions_plmc = inst.calculate_contributed_plmc_spent( +// remainder_contributions.clone(), +// project_metadata.minimum_price.clone(), +// true, +// ); +// let all_plmc = inst.generic_map_operation( +// vec![evaluations_plmc, bids_plmc, community_contributions_plmc, remainder_contributions_plmc], +// MergeOperation::Add, +// ); +// inst.mint_plmc_to(all_plmc); +// +// let bids_usdt = inst.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( +// &bids, +// project_metadata.clone(), +// None, +// ); +// let community_contributions_usdt = inst.calculate_contributed_funding_asset_spent( +// community_contributions.clone(), +// project_metadata.minimum_price.clone(), +// ); +// let remainder_contributions_usdt = inst.calculate_contributed_funding_asset_spent( +// remainder_contributions.clone(), +// project_metadata.minimum_price.clone(), +// ); +// let all_usdt = inst.generic_map_operation( +// vec![bids_usdt, community_contributions_usdt, remainder_contributions_usdt], +// MergeOperation::Add, +// ); +// inst.mint_foreign_asset_to(all_usdt); +// +// inst.evaluate_for_users(project_id, evaluations[..1].to_vec()).unwrap(); +// for evaluation in evaluations[1..].to_vec() { +// let jwt = get_mock_jwt_with_cid(evaluation.account, InvestorType::Retail, did_user.clone(), cid.clone()); +// inst.execute(|| { +// PolimecFunding::evaluate(RuntimeOrigin::signed(evaluation.account), jwt, project_id, evaluation.usd_amount) +// .unwrap(); +// }); +// } +// +// inst.start_auction(project_id, ISSUER_1).unwrap(); +// +// inst.bid_for_users(project_id, bids[..1].to_vec()).unwrap(); +// for bid in bids[1..].to_vec() { +// let jwt = get_mock_jwt_with_cid(bid.bidder, InvestorType::Institutional, did_user.clone(), cid.clone()); +// inst.execute(|| { +// PolimecFunding::bid( +// RuntimeOrigin::signed(bid.bidder), +// jwt, +// project_id, +// bid.amount, +// bid.multiplier, +// bid.asset, +// ) +// .unwrap(); +// }); +// } +// +// inst.start_community_funding(project_id).unwrap(); +// +// inst.contribute_for_users(project_id, community_contributions).unwrap(); +// +// inst.start_remainder_or_end_funding(project_id).unwrap(); +// +// for contribution in remainder_contributions { +// let jwt = +// get_mock_jwt_with_cid(contribution.contributor, InvestorType::Professional, did_user.clone(), cid.clone()); +// inst.execute(|| { +// PolimecFunding::contribute( +// RuntimeOrigin::signed(contribution.contributor), +// jwt, +// project_id, +// contribution.amount, +// contribution.multiplier, +// contribution.asset, +// ) +// .unwrap(); +// }); +// } +// +// inst.finish_funding(project_id, None).unwrap(); +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let items = +// TestRuntime::all_project_participations_by_did(&TestRuntime, block_hash, project_id, did_user).unwrap(); +// dbg!(items); +// }); +// } +// +// #[test] +// fn usd_target_percent_reached() { +// let inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let (inst, project_id_1) = +// create_finished_project_with_usd_raised(inst, 2_000_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// let (inst, project_id_2) = create_finished_project_with_usd_raised(inst, 945_000 * USD_UNIT, 1_000_000 * USD_UNIT); +// let (inst, project_id_3) = create_finished_project_with_usd_raised(inst, 517_000 * USD_UNIT, 100_000 * USD_UNIT); +// +// let (mut inst, project_id_4) = create_finished_project_with_usd_raised(inst, 50_000 * USD_UNIT, 100_000 * USD_UNIT); +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let percent_200: FixedU128 = +// TestRuntime::usd_target_percent_reached(&TestRuntime, block_hash, project_id_1).unwrap(); +// assert_close_enough!( +// percent_200.into_inner(), +// FixedU128::from_float(2.0f64).into_inner(), +// Perquintill::from_float(0.999) +// ); +// +// let percent_94_5: FixedU128 = +// TestRuntime::usd_target_percent_reached(&TestRuntime, block_hash, project_id_2).unwrap(); +// assert_close_enough!( +// percent_94_5.into_inner(), +// FixedU128::from_float(0.945f64).into_inner(), +// Perquintill::from_float(0.999) +// ); +// +// let percent_517: FixedU128 = +// TestRuntime::usd_target_percent_reached(&TestRuntime, block_hash, project_id_3).unwrap(); +// assert_close_enough!( +// percent_517.into_inner(), +// FixedU128::from_float(5.17f64).into_inner(), +// Perquintill::from_float(0.999) +// ); +// +// let percent_50: FixedU128 = +// TestRuntime::usd_target_percent_reached(&TestRuntime, block_hash, project_id_4).unwrap(); +// assert_close_enough!( +// percent_50.into_inner(), +// FixedU128::from_float(0.5f64).into_inner(), +// Perquintill::from_float(0.999) +// ); +// }); +// } +// +// #[test] +// fn projects_by_did() { +// let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); +// let did_user = generate_did_from_account(420); +// +// let project_id_1 = inst.create_settled_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// Some(did_user.clone()), +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// +// let _project_id_2 = inst.create_settled_project( +// default_project_metadata(ISSUER_1), +// ISSUER_1, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// +// let project_id_3 = inst.create_settled_project( +// default_project_metadata(ISSUER_2), +// ISSUER_2, +// Some(did_user.clone()), +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// +// let _project_id_4 = inst.create_settled_project( +// default_project_metadata(ISSUER_3), +// ISSUER_3, +// None, +// default_evaluations(), +// default_bids(), +// default_community_buys(), +// default_remainder_buys(), +// ); +// +// inst.execute(|| { +// let block_hash = System::block_hash(System::block_number()); +// let project_ids = TestRuntime::projects_by_did(&TestRuntime, block_hash, did_user).unwrap(); +// assert_eq!(project_ids, vec![project_id_1, project_id_3]); +// }); +// } diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index ddae6c917..15aa6f2c2 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -699,7 +699,7 @@ pub mod inner_types { Application, EvaluationRound, AuctionInitializePeriod, - Auction, + AuctionRound, CommunityRound(BlockNumber), FundingFailed, FundingSuccessful, @@ -711,8 +711,8 @@ pub mod inner_types { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, Serialize, Deserialize)] pub enum FundingOutcome { - FundingSuccessful, - FundingFailed, + Success, + Failure, } #[derive(Default, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] @@ -788,35 +788,19 @@ pub mod inner_types { Migration, } - /// An enum representing all possible outcomes for a project. - #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub enum ProjectOutcome { - /// 90%+ of the funding target was reached, so the project is successful. - FundingSuccessful, - /// 33%- of the funding target was reached, so the project failed. - FundingFailed, - } - #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct EvaluationRoundInfo { pub total_bonded_usd: Balance, pub total_bonded_plmc: Balance, - pub evaluators_outcome: EvaluatorsOutcome, + pub evaluators_outcome: Option>, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum EvaluatorsOutcome { - Unchanged, Rewarded(RewardInfo), Slashed, } - #[derive(Clone, Copy, Encode, Decode, Eq, PartialEq, PartialOrd, Ord, RuntimeDebug, TypeInfo, MaxEncodedLen)] - pub enum RewardOrSlash { - Reward(Balance), - Slash(Balance), - } - #[derive(Default, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct RewardInfo { // Total "Early Evaluators" rewards amount in Contribution Tokens diff --git a/runtimes/polimec/src/lib.rs b/runtimes/polimec/src/lib.rs index 7321e4138..2630af9e8 100644 --- a/runtimes/polimec/src/lib.rs +++ b/runtimes/polimec/src/lib.rs @@ -245,12 +245,9 @@ impl Contains for BaseCallFilter { pallet_funding::Call::start_auction { .. } | pallet_funding::Call::bid { .. } | pallet_funding::Call::contribute { .. } | - pallet_funding::Call::settle_successful_evaluation { .. } | - pallet_funding::Call::settle_failed_evaluation { .. } | - pallet_funding::Call::settle_successful_bid { .. } | - pallet_funding::Call::settle_failed_bid { .. } | - pallet_funding::Call::settle_successful_contribution { .. } | - pallet_funding::Call::settle_failed_contribution { .. } + pallet_funding::Call::settle_evaluation { .. } | + pallet_funding::Call::settle_bid { .. } | + pallet_funding::Call::settle_contribution { .. } ) }, _ => true,