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..f9671d72f 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,7 @@ 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); 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 253393e2f..5097f016c 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::{Inspect, MutateHold as FungibleMutateHold}, + fungible::MutateHold as FungibleMutateHold, fungibles::Mutate as FungiblesMutate, - tokens::{DepositConsequence, Fortitude, Precision, Preservation, Provenance, Restriction}, + tokens::{Fortitude, Precision, Preservation, Restriction}, Get, }, }; @@ -72,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 { @@ -80,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 { @@ -90,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( @@ -126,7 +122,7 @@ impl Pallet { &evaluation.evaluator, evaluation.id, ParticipationType::Evaluation, - reward, + ct_rewarded, duration, )?; } @@ -136,117 +132,71 @@ 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/bid is partially accepted + 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, }); @@ -256,151 +206,89 @@ impl Pallet { /// Calculate the amount of funds the bidder 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(()) @@ -449,6 +337,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(), @@ -465,6 +356,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/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 b0cca3ff1..a0931d497 100644 --- a/runtimes/polimec/src/lib.rs +++ b/runtimes/polimec/src/lib.rs @@ -56,7 +56,7 @@ use sp_runtime::{ IdentifyAccount, IdentityLookup, OpaqueKeys, Verify, }, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, MultiSignature, Perquintill, SaturatedConversion, + ApplyExtrinsicResult, FixedU128, MultiSignature, SaturatedConversion, }; use sp_std::{cmp::Ordering, prelude::*}; use sp_version::RuntimeVersion; @@ -240,7 +240,6 @@ impl Contains for BaseCallFilter { pallet_funding::Call::remove_project { .. } | pallet_funding::Call::edit_project { .. } | pallet_funding::Call::start_evaluation { .. } | - pallet_funding::Call::end_evaluation { .. } | pallet_funding::Call::evaluate { .. } | pallet_funding::Call::end_evaluation { .. } | pallet_funding::Call::start_auction { .. } | @@ -249,12 +248,9 @@ impl Contains for BaseCallFilter { pallet_funding::Call::contribute { .. } | pallet_funding::Call::end_funding { .. } | pallet_funding::Call::start_settlement { .. } | - 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,