diff --git a/integration-tests/src/tests/credentials.rs b/integration-tests/src/tests/credentials.rs index e3f41c438..47f0892b3 100644 --- a/integration-tests/src/tests/credentials.rs +++ b/integration-tests/src/tests/credentials.rs @@ -19,9 +19,12 @@ use frame_support::{assert_err, assert_ok, dispatch::GetDispatchInfo, traits::to use macros::generate_accounts; use polimec_common::credentials::InvestorType; use polimec_common_test_utils::{get_fake_jwt, get_test_jwt}; -use sp_runtime::{generic::Era, traits::SignedExtension, AccountId32, DispatchError}; -use sp_runtime::transaction_validity::InvalidTransaction::Payment; -use sp_runtime::transaction_validity::TransactionValidityError; +use sp_runtime::{ + generic::Era, + traits::SignedExtension, + transaction_validity::{InvalidTransaction::Payment, TransactionValidityError}, + AccountId32, DispatchError, +}; use tests::defaults::*; #[test] @@ -33,7 +36,7 @@ fn test_jwt_for_create() { let retail_jwt = get_test_jwt(PolitestAccountId::from(ISSUER), InvestorType::Retail); assert_noop!( PolitestFundingPallet::create_project(PolitestOrigin::signed(ISSUER.into()), retail_jwt, project.clone()), - pallet_funding::Error::::NotAllowed + pallet_funding::Error::::WrongInvestorType ); let inst_jwt = get_test_jwt(PolitestAccountId::from(ISSUER), InvestorType::Institutional); assert_ok!(PolitestFundingPallet::create_project( @@ -69,7 +72,7 @@ fn dispenser_signed_extensions_pass_for_new_account() { let jwt = get_test_jwt(who.clone(), InvestorType::Retail); let free_call = PolitestCall::Dispenser(pallet_dispenser::Call::dispense { jwt: jwt.clone() }); - let paid_call = PolitestCall::System(frame_system::Call::remark{remark: vec![69, 69]}); + let paid_call = PolitestCall::System(frame_system::Call::remark { remark: vec![69, 69] }); let extra: politest_runtime::SignedExtra = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), @@ -80,8 +83,14 @@ fn dispenser_signed_extensions_pass_for_new_account() { frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0u64.into()).into(), ); - assert_err!(extra.validate(&who, &paid_call, &paid_call.get_dispatch_info(), 0), TransactionValidityError::Invalid(Payment)); - assert_err!(extra.clone().pre_dispatch(&who, &paid_call, &paid_call.get_dispatch_info(), 0), TransactionValidityError::Invalid(Payment)); + assert_err!( + extra.validate(&who, &paid_call, &paid_call.get_dispatch_info(), 0), + TransactionValidityError::Invalid(Payment) + ); + assert_err!( + extra.clone().pre_dispatch(&who, &paid_call, &paid_call.get_dispatch_info(), 0), + TransactionValidityError::Invalid(Payment) + ); assert_ok!(extra.validate(&who, &free_call, &free_call.get_dispatch_info(), 0)); assert_ok!(extra.pre_dispatch(&who, &free_call, &free_call.get_dispatch_info(), 0)); diff --git a/pallets/funding/README.md b/pallets/funding/README.md index 18ca65c8a..4c9be08cb 100644 --- a/pallets/funding/README.md +++ b/pallets/funding/README.md @@ -108,72 +108,6 @@ directly. This is useful if you need to make use of this pallet's functionalities in a pallet of your own, and you don't want to pay the transaction fees twice. -### Example: A retail user buying tokens for a project in the community round - -```rust -pub use pallet::*; - -#[cfg(test)] -mod tests; - -#[cfg(test)] -mod mock; - -use pallet_funding::{self as funding}; - -#[frame_support::pallet(dev_mode)] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config + funding::Config {} - - #[pallet::call] - impl Pallet { - /// Buy tokens for a project in the community round if it achieved at least 500k USDT funding - #[pallet::weight(0)] - pub fn buy_if_popular( - origin: OriginFor, - project_id: ::ProjectIdParameter, - amount: ::CurrencyBalance - ) -> DispatchResult { - let retail_user = ensure_signed(origin)?; - let project_id: ::ProjectIdentifier = project_id.into(); - // Check project is in the community round - let project_info = funding::Pallet::::project_info(project_id).ok_or(Error::::ProjectNotFound)?; - ensure!(project_info.project_status == funding::ProjectStatus::CommunityRound, "Project is not in the community round"); - - // Calculate how much funding was done already - let project_contributions: ::CurrencyBalance = funding::Contributions::::iter_prefix_values(project_id) - .flatten() - .fold( - 0u64.into(), - |total_tokens_bought, contribution| { - total_tokens_bought + contribution.contribution_amount - } - ); - - ensure!(project_contributions >= 500_000_0_000_000_000u64.into(), "Project did not achieve at least 500k USDT funding"); - - // Buy tokens with the default multiplier - >::do_contribute(retail_user, project_id, amount, None)?; - - Ok(()) - } - } - - #[pallet::error] - pub enum Error { - ProjectNotFound, - } -} -``` - ## Credentials The pallet will only allow users with certain credential types, to execute diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index e7ce581d7..3a7c94d97 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -67,15 +67,23 @@ impl Pallet { #[transactional] pub fn do_start_evaluation(caller: AccountIdOf, project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let now = >::block_number(); // * Validity checks * - ensure!(project_details.issuer_account == caller, Error::::NotAllowed); - ensure!(project_details.status == ProjectStatus::Application, Error::::ProjectNotInApplicationRound); - ensure!(!project_details.is_frozen, Error::::ProjectAlreadyFrozen); - ensure!(project_metadata.offchain_information_hash.is_some(), Error::::MetadataNotProvided); + ensure!(project_details.issuer_account == caller, Error::::IssuerError(IssuerErrorReason::NotIssuer)); + ensure!( + project_details.status == ProjectStatus::Application, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); + ensure!(!project_details.is_frozen, Error::::ProjectError(ProjectErrorReason::ProjectAlreadyFrozen)); + ensure!( + project_metadata.offchain_information_hash.is_some(), + Error::::BadMetadata(MetadataError::MetadataNotProvided) + ); // * Calculate new variables * let evaluation_end_block = now + T::EvaluationDuration::get(); @@ -97,7 +105,7 @@ impl Pallet { actual_weight: Some(WeightInfoOf::::start_evaluation(insertions)), pays_fee: Pays::Yes, }, - error: Error::::TooManyInsertionAttempts.into(), + error: Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into(), }), }; @@ -141,15 +149,22 @@ impl Pallet { #[transactional] pub fn do_evaluation_end(project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let now = >::block_number(); - let evaluation_end_block = - project_details.phase_transition_points.evaluation.end().ok_or(Error::::FieldIsNone)?; + let evaluation_end_block = project_details + .phase_transition_points + .evaluation + .end() + .ok_or(Error::::ProjectRoundError(RoundError::TransitionPointNotSet))?; let fundraising_target_usd = project_details.fundraising_target; // * Validity checks * - ensure!(project_details.status == ProjectStatus::EvaluationRound, Error::::ProjectNotInEvaluationRound); - ensure!(now > evaluation_end_block, Error::::EvaluationPeriodNotEnded); + ensure!( + project_details.status == ProjectStatus::EvaluationRound, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); + ensure!(now > evaluation_end_block, Error::::ProjectRoundError(RoundError::TooEarlyForRound)); // * Calculate new variables * let usd_total_amount_bonded = project_details.evaluation_round_info.total_bonded_usd; @@ -177,7 +192,8 @@ impl Pallet { (&project_id, UpdateType::AuctionOpeningStart), ) { Ok(insertions) => insertions, - Err(_insertions) => return Err(Error::::TooManyInsertionAttempts.into()), + Err(_insertions) => + return Err(Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into()), }; // * Emit events * @@ -238,28 +254,32 @@ impl Pallet { #[transactional] pub fn do_auction_opening(caller: AccountIdOf, project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let now = >::block_number(); let auction_initialize_period_start_block = project_details .phase_transition_points .auction_initialize_period .start() - .ok_or(Error::::EvaluationPeriodNotEnded)?; + .ok_or(Error::::ProjectRoundError(RoundError::TransitionPointNotSet))?; // * Validity checks * ensure!( caller == T::PalletId::get().into_account_truncating() || caller == project_details.issuer_account, - Error::::NotAllowed + Error::::IssuerError(IssuerErrorReason::NotIssuer) ); - ensure!(now >= auction_initialize_period_start_block, Error::::TooEarlyForAuctionOpeningStart); + ensure!( + now >= auction_initialize_period_start_block, + Error::::ProjectRoundError(RoundError::TooEarlyForRound) + ); // If the auction is first manually started, the automatic transition fails here. This // behaviour is intended, as it gracefully skips the automatic transition if the // auction was started manually. ensure!( project_details.status == ProjectStatus::AuctionInitializePeriod, - Error::::ProjectNotInAuctionInitializePeriodRound + Error::::ProjectRoundError(RoundError::IncorrectRound) ); // * Calculate new variables * @@ -287,7 +307,7 @@ impl Pallet { actual_weight: Some(WeightInfoOf::::start_auction_manually(insertion_attempts)), pays_fee: Pays::Yes, }, - error: Error::::TooManyInsertionAttempts.into(), + error: Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into(), }), }; @@ -326,14 +346,21 @@ impl Pallet { #[transactional] pub fn do_auction_closing(project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let now = >::block_number(); - let opening_end_block = - project_details.phase_transition_points.auction_opening.end().ok_or(Error::::FieldIsNone)?; + let opening_end_block = project_details + .phase_transition_points + .auction_opening + .end() + .ok_or(Error::::ProjectRoundError(RoundError::TransitionPointNotSet))?; // * Validity checks * - ensure!(now > opening_end_block, Error::::TooEarlyForAuctionClosingStart); - ensure!(project_details.status == ProjectStatus::AuctionOpening, Error::::ProjectNotInAuctionOpeningRound); + ensure!(now > opening_end_block, Error::::ProjectRoundError(RoundError::TooEarlyForRound)); + ensure!( + project_details.status == ProjectStatus::AuctionOpening, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); // * Calculate new variables * let closing_start_block = now + 1u32.into(); @@ -352,7 +379,7 @@ impl Pallet { (&project_id, UpdateType::CommunityFundingStart), ) { Ok(iterations) => iterations, - Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), + Err(_iterations) => return Err(Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into()), }; // * Emit events * @@ -389,17 +416,28 @@ impl Pallet { #[transactional] pub fn do_community_funding(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::::ProjectDetailsNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; let now = >::block_number(); - let auction_closing_start_block = - project_details.phase_transition_points.auction_closing.start().ok_or(Error::::FieldIsNone)?; - let auction_closing_end_block = - project_details.phase_transition_points.auction_closing.end().ok_or(Error::::FieldIsNone)?; + let auction_closing_start_block = project_details + .phase_transition_points + .auction_closing + .start() + .ok_or(Error::::ProjectRoundError(RoundError::TransitionPointNotSet))?; + let auction_closing_end_block = project_details + .phase_transition_points + .auction_closing + .end() + .ok_or(Error::::ProjectRoundError(RoundError::TransitionPointNotSet))?; // * Validity checks * - ensure!(now > auction_closing_end_block, Error::::TooEarlyForCommunityRoundStart); - ensure!(project_details.status == ProjectStatus::AuctionClosing, Error::::ProjectNotInAuctionClosingRound); + ensure!(now > auction_closing_end_block, Error::::ProjectRoundError(RoundError::TooEarlyForRound)); + ensure!( + project_details.status == ProjectStatus::AuctionClosing, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); // * Calculate new variables * let end_block = Self::select_random_block(auction_closing_start_block, auction_closing_end_block); @@ -411,7 +449,8 @@ impl Pallet { end_block, project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size, ); - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; match calculation_result { Err(e) => return Err(DispatchErrorWithPostInfo { post_info: ().into(), error: e }), Ok((accepted_bids_count, rejected_bids_count)) => { @@ -429,7 +468,8 @@ impl Pallet { (&project_id, UpdateType::RemainderFundingStart), ) { Ok(iterations) => iterations, - Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), + Err(_iterations) => + return Err(Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into()), }; // * Emit events * @@ -474,20 +514,27 @@ impl Pallet { #[transactional] pub fn do_remainder_funding(project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let now = >::block_number(); - let community_end_block = - project_details.phase_transition_points.community.end().ok_or(Error::::FieldIsNone)?; + let community_end_block = project_details + .phase_transition_points + .community + .end() + .ok_or(Error::::ProjectRoundError(RoundError::TransitionPointNotSet))?; // * Validity checks * - ensure!(now > community_end_block, Error::::TooEarlyForRemainderRoundStart); - ensure!(project_details.status == ProjectStatus::CommunityRound, Error::::ProjectNotInCommunityRound); + ensure!(now > community_end_block, Error::::ProjectRoundError(RoundError::TooEarlyForRound)); + ensure!( + project_details.status == ProjectStatus::CommunityRound, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); // Transition to remainder round was initiated by `do_community_funding`, but the ct // tokens where already sold in the community round. This transition is obsolete. ensure!( project_details.remaining_contribution_tokens > 0u32.into(), - Error::::RoundTransitionAlreadyHappened + Error::::ProjectRoundError(RoundError::RoundTransitionAlreadyHappened) ); // * Calculate new variables * @@ -505,7 +552,8 @@ impl Pallet { let insertion_iterations = match Self::add_to_update_store(remainder_end_block + 1u32.into(), (&project_id, UpdateType::FundingEnd)) { Ok(iterations) => iterations, - Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), + Err(_iterations) => + return Err(Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into()), }; // * Emit events * @@ -549,8 +597,10 @@ impl Pallet { #[transactional] pub fn do_end_funding(project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; let remaining_cts = project_details.remaining_contribution_tokens; let remainder_end_block = project_details.phase_transition_points.remainder.end(); let now = >::block_number(); @@ -564,7 +614,7 @@ impl Pallet { project_details.status == ProjectStatus::AuctionClosing || // or the last funding round ending matches!(remainder_end_block, Some(end_block) if now > end_block), - Error::::TooEarlyForFundingEnd + Error::::ProjectRoundError(RoundError::TooEarlyForRound) ); // do_end_funding was already executed, but automatic transition was included in the // do_remainder_funding function. We gracefully skip the this transition. @@ -575,7 +625,7 @@ impl Pallet { ProjectStatus::FundingFailed | ProjectStatus::AwaitingProjectDecision ), - Error::::RoundTransitionAlreadyHappened + Error::::ProjectRoundError(RoundError::RoundTransitionAlreadyHappened) ); // * Calculate new variables * @@ -606,7 +656,8 @@ impl Pallet { (&project_id, UpdateType::ProjectDecision(FundingOutcomeDecision::AcceptFunding)), ) { Ok(iterations) => iterations, - Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), + Err(_iterations) => + return Err(Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into()), }; ProjectsDetails::::insert(project_id, project_details); Ok(PostDispatchInfo { @@ -623,7 +674,8 @@ impl Pallet { (&project_id, UpdateType::ProjectDecision(FundingOutcomeDecision::AcceptFunding)), ) { Ok(iterations) => iterations, - Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), + Err(_iterations) => + return Err(Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into()), }; ProjectsDetails::::insert(project_id, project_details); Ok(PostDispatchInfo { @@ -655,10 +707,11 @@ impl Pallet { #[transactional] pub fn do_project_decision(project_id: ProjectId, decision: FundingOutcomeDecision) -> DispatchResultWithPostInfo { // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; ensure!( project_details.status == ProjectStatus::AwaitingProjectDecision, - Error::::RoundTransitionAlreadyHappened + Error::::ProjectRoundError(RoundError::RoundTransitionAlreadyHappened) ); let outcome = match decision { FundingOutcomeDecision::AcceptFunding => ProjectOutcome::FundingAccepted, @@ -673,16 +726,18 @@ impl Pallet { #[transactional] pub fn do_start_settlement(project_id: ProjectId) -> DispatchResultWithPostInfo { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let token_information = - ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?.token_information; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + let token_information = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))? + .token_information; let now = >::block_number(); // * Validity checks * ensure!( project_details.status == ProjectStatus::FundingSuccessful || project_details.status == ProjectStatus::FundingFailed, - Error::::NotAllowed + Error::::ProjectRoundError(RoundError::IncorrectRound) ); // * Calculate new variables * @@ -744,15 +799,7 @@ impl Pallet { did: Did, ) -> Result<(ProjectDetailsOf, BucketOf), DispatchError> { if let Err(error) = metadata.is_valid() { - let metadata_error = match error { - ValidityError::PriceTooLow => MetadataError::PriceTooLow, - ValidityError::TicketSizeError => MetadataError::TicketSizeError, - ValidityError::ParticipationCurrenciesError => MetadataError::ParticipationCurrenciesError, - ValidityError::AllocationSizeError => MetadataError::AllocationSizeError, - ValidityError::AuctionRoundPercentageError => MetadataError::AuctionRoundPercentageError, - ValidityError::FundingTargetTooLow => MetadataError::FundingTargetTooLow, - }; - return Err(Error::::BadMetadata(metadata_error).into()); + return Err(Error::::BadMetadata(error).into()); } let total_allocation_size = metadata.total_allocation_size; @@ -819,7 +866,7 @@ impl Pallet { let maybe_active_project = DidWithActiveProjects::::get(did.clone()); // * Validity checks * - ensure!(maybe_active_project == None, Error::::IssuerHasActiveProjectAlready); + ensure!(maybe_active_project == None, Error::::IssuerError(IssuerErrorReason::HasActiveProject)); let (project_details, bucket) = Self::project_validation(&initial_metadata, issuer.clone(), did.clone())?; @@ -833,7 +880,7 @@ impl Pallet { ::ExistentialDeposit::get(), Preservation::Preserve, ) - .map_err(|_| Error::::NotEnoughFundsForEscrowCreation)?; + .map_err(|_| Error::::IssuerError(IssuerErrorReason::NotEnoughFunds))?; // * Update storage * ProjectsMetadata::::insert(project_id, &initial_metadata); @@ -851,11 +898,12 @@ impl Pallet { #[transactional] pub fn do_remove_project(issuer: AccountIdOf, project_id: ProjectId, did: Did) -> DispatchResult { // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; // * Validity checks * - ensure!(project_details.issuer_account == issuer, Error::::NotAllowed); - ensure!(project_details.is_frozen.not(), Error::::Frozen); + ensure!(project_details.issuer_account == issuer, Error::::IssuerError(IssuerErrorReason::NotIssuer)); + ensure!(project_details.is_frozen.not(), Error::::ProjectError(ProjectErrorReason::ProjectIsFrozen)); // * Update storage * ProjectsDetails::::remove(project_id); @@ -886,11 +934,12 @@ impl Pallet { new_project_metadata: ProjectMetadataOf, ) -> DispatchResult { // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; // * Validity checks * - ensure!(project_details.issuer_account == issuer, Error::::NotAllowed); - ensure!(!project_details.is_frozen, Error::::Frozen); + ensure!(project_details.issuer_account == issuer, Error::::IssuerError(IssuerErrorReason::NotIssuer)); + ensure!(!project_details.is_frozen, Error::::ProjectError(ProjectErrorReason::ProjectIsFrozen)); // * Calculate new variables * let (project_details, bucket) = @@ -917,21 +966,31 @@ impl Pallet { investor_type: InvestorType, ) -> DispatchResultWithPostInfo { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let now = >::block_number(); let evaluation_id = NextEvaluationId::::get(); let caller_existing_evaluations: Vec<(u32, EvaluationInfoOf)> = Evaluations::::iter_prefix((project_id, evaluator)).collect(); - let plmc_usd_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PLMCPriceNotAvailable)?; + let plmc_usd_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PriceNotFound)?; let early_evaluation_reward_threshold_usd = T::EvaluationSuccessThreshold::get() * project_details.fundraising_target; let evaluation_round_info = &mut project_details.evaluation_round_info; let evaluations_count = EvaluationCounts::::get(project_id); // * Validity Checks * - ensure!(project_details.issuer_did != did, Error::::ParticipationToThemselves); - ensure!(project_details.status == ProjectStatus::EvaluationRound, Error::::ProjectNotInEvaluationRound); - ensure!(evaluations_count < T::MaxEvaluationsPerProject::get(), Error::::TooManyEvaluationsForProject); + ensure!( + project_details.issuer_did != did, + Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) + ); + ensure!( + project_details.status == ProjectStatus::EvaluationRound, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); + ensure!( + evaluations_count < T::MaxEvaluationsPerProject::get(), + Error::::ParticipationFailed(ParticipationError::TooManyProjectParticipations) + ); // * Calculate new variables * if investor_type == InvestorType::Retail { @@ -979,7 +1038,10 @@ impl Pallet { .min_by_key(|(_, evaluation)| evaluation.original_plmc_bond) .ok_or(Error::::ImpossibleState)?; - ensure!(lowest_evaluation.original_plmc_bond < plmc_bond, Error::::EvaluationBondTooLow); + ensure!( + lowest_evaluation.original_plmc_bond < plmc_bond, + Error::::ParticipationFailed(ParticipationError::TooLow) + ); ensure!( lowest_evaluation.original_plmc_bond == lowest_evaluation.current_plmc_bond, "Using evaluation funds for participating should not be possible in the evaluation round" @@ -1046,12 +1108,15 @@ impl Pallet { investor_type: InvestorType, ) -> DispatchResultWithPostInfo { // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; let plmc_usd_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PriceNotFound)?; // Fetch current bucket details and other required info - let mut current_bucket = Buckets::::get(project_id).ok_or(Error::::ProjectNotFound)?; + let mut current_bucket = + Buckets::::get(project_id).ok_or(Error::::ProjectError(ProjectErrorReason::BucketNotFound))?; let now = >::block_number(); let mut amount_to_bid = ct_amount; let total_bids_for_project = BidCounts::::get(project_id); @@ -1068,13 +1133,13 @@ impl Pallet { let metadata_bidder_ticket_size_bounds = match investor_type { InvestorType::Institutional => project_metadata.bidding_ticket_sizes.institutional, InvestorType::Professional => project_metadata.bidding_ticket_sizes.professional, - _ => return Err(Error::::NotAllowed.into()), + _ => return Err(Error::::WrongInvestorType.into()), }; let max_multiplier = match investor_type { InvestorType::Professional => PROFESSIONAL_MAX_MULTIPLIER, InvestorType::Institutional => INSTITUTIONAL_MAX_MULTIPLIER, // unreachable - _ => return Err(Error::::NotAllowed.into()), + _ => return Err(Error::::ImpossibleState.into()), }; // * Validity checks * @@ -1083,27 +1148,37 @@ impl Pallet { DispatchError::from("Retail investors are not allowed to bid") ); - ensure!(ct_amount > Zero::zero(), Error::::BidTooLow); - ensure!(did != project_details.issuer_did, Error::::ParticipationToThemselves); + ensure!(ct_amount > Zero::zero(), Error::::ParticipationFailed(ParticipationError::TooLow)); + ensure!( + did != project_details.issuer_did, + Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) + ); ensure!( matches!(project_details.status, ProjectStatus::AuctionOpening | ProjectStatus::AuctionClosing), - Error::::AuctionNotStarted + Error::::ProjectRoundError(RoundError::IncorrectRound) ); ensure!( project_metadata.participation_currencies.contains(&funding_asset), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); ensure!( metadata_bidder_ticket_size_bounds.usd_ticket_above_minimum_per_participation(min_total_ticket_size), - Error::::BidTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) + ); + ensure!( + multiplier.into() <= max_multiplier && multiplier.into() > 0u8, + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); - ensure!(multiplier.into() <= max_multiplier && multiplier.into() > 0u8, Error::::ForbiddenMultiplier); // Note: We limit the CT Amount to the auction allocation size, to avoid long running loops. ensure!( ct_amount <= project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size, - Error::::NotAllowed + Error::::ParticipationFailed(ParticipationError::TooHigh) + ); + ensure!( + existing_bids.len() < T::MaxBidsPerUser::get() as usize, + Error::::ParticipationFailed(ParticipationError::TooManyUserParticipations) ); // While there's a remaining amount to bid for @@ -1171,10 +1246,16 @@ impl Pallet { ensure!( metadata_ticket_size_bounds .usd_ticket_below_maximum_per_did(total_usd_bid_by_did.saturating_add(ticket_size)), - Error::::BidTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) + ); + ensure!( + total_bids_by_bidder < T::MaxBidsPerUser::get(), + Error::::ParticipationFailed(ParticipationError::TooManyUserParticipations) + ); + ensure!( + total_bids_for_project < T::MaxBidsPerProject::get(), + Error::::ParticipationFailed(ParticipationError::TooManyProjectParticipations) ); - ensure!(total_bids_by_bidder < T::MaxBidsPerUser::get(), Error::::TooManyBidsForUser); - ensure!(total_bids_for_project < T::MaxBidsPerProject::get(), Error::::TooManyBidsForProject); let funding_asset_usd_price = T::PriceProvider::get_price(funding_asset.to_assethub_id()).ok_or(Error::::PriceNotFound)?; @@ -1246,11 +1327,15 @@ impl Pallet { did: Did, investor_type: InvestorType, ) -> DispatchResultWithPostInfo { - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let did_has_winning_bid = DidWithWinningBids::::get(project_id, did.clone()); - ensure!(project_details.status == ProjectStatus::CommunityRound, Error::::AuctionNotStarted); - ensure!(!did_has_winning_bid, Error::::UserHasWinningBids); + ensure!( + project_details.status == ProjectStatus::CommunityRound, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); + ensure!(!did_has_winning_bid, Error::::ParticipationFailed(ParticipationError::UserHasWinningBid)); let buyable_tokens = token_amount.min(project_details.remaining_contribution_tokens); project_details.remaining_contribution_tokens.saturating_reduce(buyable_tokens); @@ -1286,9 +1371,13 @@ impl Pallet { did: Did, investor_type: InvestorType, ) -> DispatchResultWithPostInfo { - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; - ensure!(project_details.status == ProjectStatus::RemainderRound, Error::::AuctionNotStarted); + ensure!( + project_details.status == ProjectStatus::RemainderRound, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); let buyable_tokens = token_amount.min(project_details.remaining_contribution_tokens); let before = project_details.remaining_contribution_tokens; @@ -1318,12 +1407,14 @@ impl Pallet { investor_type: InvestorType, did: Did, ) -> DispatchResultWithPostInfo { - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; let caller_existing_contributions = Contributions::::iter_prefix_values((project_id, contributor)).collect::>(); let total_usd_bought_by_did = ContributionBoughtUSD::::get((project_id, did.clone())); let now = >::block_number(); - let ct_usd_price = project_details.weighted_average_price.ok_or(Error::::AuctionNotStarted)?; + let ct_usd_price = + project_details.weighted_average_price.ok_or(Error::::ProjectError(ProjectErrorReason::WapNotSet))?; let plmc_usd_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PriceNotFound)?; let funding_asset_usd_price = T::PriceProvider::get_price(funding_asset.to_assethub_id()).ok_or(Error::::PriceNotFound)?; @@ -1349,40 +1440,49 @@ impl Pallet { InvestorType::Institutional => INSTITUTIONAL_MAX_MULTIPLIER, }; // * Validity checks * - ensure!(multiplier.into() <= max_multiplier && multiplier.into() > 0u8, Error::::ForbiddenMultiplier); + ensure!( + multiplier.into() <= max_multiplier && multiplier.into() > 0u8, + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) + ); ensure!( project_metadata.participation_currencies.contains(&funding_asset), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) + ); + ensure!( + did.clone() != project_details.issuer_did, + Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); - ensure!(did.clone() != project_details.issuer_did, Error::::ParticipationToThemselves); ensure!( caller_existing_contributions.len() < T::MaxContributionsPerUser::get() as usize, - Error::::TooManyContributionsForUser + Error::::ParticipationFailed(ParticipationError::TooManyUserParticipations) ); ensure!( contributor_ticket_size.usd_ticket_above_minimum_per_participation(ticket_size), - Error::::ContributionTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); ensure!( contributor_ticket_size.usd_ticket_below_maximum_per_did(total_usd_bought_by_did + ticket_size), - Error::::ContributionTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); ensure!( project_metadata.participation_currencies.contains(&funding_asset), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) + ); + ensure!( + did.clone() != project_details.issuer_did, + Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); - ensure!(did.clone() != project_details.issuer_did, Error::::ParticipationToThemselves); ensure!( caller_existing_contributions.len() < T::MaxContributionsPerUser::get() as usize, - Error::::TooManyContributionsForUser + Error::::ParticipationFailed(ParticipationError::TooManyUserParticipations) ); ensure!( contributor_ticket_size.usd_ticket_above_minimum_per_participation(ticket_size), - Error::::ContributionTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); ensure!( contributor_ticket_size.usd_ticket_below_maximum_per_did(total_usd_bought_by_did + ticket_size), - Error::::ContributionTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); let plmc_bond = Self::calculate_plmc_bond(ticket_size, multiplier, plmc_usd_price)?; @@ -1421,7 +1521,8 @@ impl Pallet { let fully_filled_vecs_from_insertion = match Self::add_to_update_store(now + 1u32.into(), (&project_id, UpdateType::FundingEnd)) { Ok(iterations) => iterations, - Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), + Err(_iterations) => + return Err(Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into()), }; weight_round_end_flag = Some(fully_filled_vecs_from_insertion); @@ -1458,12 +1559,16 @@ impl Pallet { decision: FundingOutcomeDecision, ) -> DispatchResultWithPostInfo { // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let now = >::block_number(); // * Validity checks * - ensure!(project_details.issuer_account == issuer, Error::::NotAllowed); - ensure!(project_details.status == ProjectStatus::AwaitingProjectDecision, Error::::NotAllowed); + ensure!(project_details.issuer_account == issuer, Error::::IssuerError(IssuerErrorReason::NotIssuer)); + ensure!( + project_details.status == ProjectStatus::AwaitingProjectDecision, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); // * Update storage * let insertion_attempts: u32; @@ -1475,7 +1580,7 @@ impl Pallet { actual_weight: Some(WeightInfoOf::::decide_project_outcome(iterations)), pays_fee: Pays::Yes, }, - error: Error::::TooManyInsertionAttempts.into(), + error: Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into(), }), }; @@ -1494,10 +1599,11 @@ impl Pallet { para_id: ParaId, ) -> DispatchResult { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; // * Validity checks * - ensure!(&(project_details.issuer_account) == caller, Error::::NotAllowed); + ensure!(&(project_details.issuer_account) == caller, Error::::IssuerError(IssuerErrorReason::NotIssuer)); // * Update storage * project_details.parachain_id = Some(para_id); @@ -1634,7 +1740,8 @@ impl Pallet { #[transactional] pub fn do_start_migration_readiness_check(caller: &AccountIdOf, project_id: ProjectId) -> DispatchResult { // * Get variables * - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let mut project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let parachain_id: u32 = project_details.parachain_id.ok_or(Error::::ImpossibleState)?.into(); let project_multilocation = ParentThen(X1(Parachain(parachain_id))); let now = >::block_number(); @@ -1643,14 +1750,17 @@ impl Pallet { let max_weight = Weight::from_parts(700_000_000, 10_000); // * Validity checks * - ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); + ensure!( + project_details.status == ProjectStatus::FundingSuccessful, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); ensure!( project_details.hrmp_channel_status == HRMPChannelStatus { project_to_polimec: ChannelStatus::Open, polimec_to_project: ChannelStatus::Open }, - Error::::CommsNotEstablished + Error::::MigrationFailed(MigrationError::ChannelNotOpen) ); if project_details.migration_readiness_check.is_none() { ensure!(caller.clone() == T::PalletId::get().into_account_truncating(), Error::::NotAllowed); @@ -1662,7 +1772,7 @@ impl Pallet { .. }) ) { - ensure!(caller == &project_details.issuer_account, Error::::NotAllowed); + ensure!(caller == &project_details.issuer_account, Error::::IssuerError(IssuerErrorReason::NotIssuer)); } // * Update storage * @@ -1711,7 +1821,8 @@ impl Pallet { }, DepositAsset { assets: Wild(All), beneficiary: ParentThen(Parachain(POLIMEC_PARA_ID).into()).into() }, ]); - >::send_xcm(Here, project_multilocation, xcm).map_err(|_| Error::::XcmFailed)?; + >::send_xcm(Here, project_multilocation, xcm) + .map_err(|_| Error::::MigrationFailed(MigrationError::XcmFailed))?; // * Emit events * Self::deposit_event(Event::::MigrationReadinessCheckStarted { project_id, caller: caller.clone() }); @@ -1738,18 +1849,22 @@ impl Pallet { } None }) - .ok_or(Error::::NotAllowed)?; + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let para_id = if let MultiLocation { parents: 1, interior: X1(Parachain(para_id)) } = location { ParaId::from(para_id) } else { - return Err(Error::::NotAllowed.into()); + return Err(Error::::MigrationFailed(MigrationError::WrongParaId).into()); }; - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; let contribution_tokens_sold = project_metadata.total_allocation_size.saturating_sub(project_details.remaining_contribution_tokens); - ensure!(project_details.parachain_id == Some(para_id), Error::::NotAllowed); + ensure!( + project_details.parachain_id == Some(para_id), + Error::::MigrationFailed(MigrationError::WrongParaId) + ); match (response.clone(), migration_check) { ( @@ -1804,16 +1919,22 @@ impl Pallet { #[transactional] pub fn do_migrate_one_participant(project_id: ProjectId, participant: AccountIdOf) -> DispatchResult { // * Get variables * - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let migration_readiness_check = project_details.migration_readiness_check.ok_or(Error::::NotAllowed)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + let migration_readiness_check = project_details + .migration_readiness_check + .ok_or(Error::::MigrationFailed(MigrationError::ChannelNotReady))?; let project_para_id = project_details.parachain_id.ok_or(Error::::ImpossibleState)?; let now = >::block_number(); - ensure!(Self::user_has_no_participations(project_id, participant.clone()), Error::::NotAllowed); - let (_, migrations) = - UserMigrations::::get(project_id, participant.clone()).ok_or(Error::::NoMigrationsFound)?; + ensure!( + Self::user_has_no_participations(project_id, participant.clone()), + Error::::MigrationFailed(MigrationError::ParticipationsNotSettled) + ); + let (_, migrations) = UserMigrations::::get(project_id, participant.clone()) + .ok_or(Error::::MigrationFailed(MigrationError::NoMigrationsFound))?; // * Validity Checks * - ensure!(migration_readiness_check.is_ready(), Error::::NotAllowed); + ensure!(migration_readiness_check.is_ready(), Error::::MigrationFailed(MigrationError::ChannelNotReady)); let project_multilocation = MultiLocation { parents: 1, interior: X1(Parachain(project_para_id.into())) }; let call: ::RuntimeCall = @@ -1826,7 +1947,8 @@ impl Pallet { // * Process Data * let xcm = Self::construct_migration_xcm_message(migrations.into(), query_id); - >::send_xcm(Here, project_multilocation, xcm).map_err(|_| Error::::XcmFailed)?; + >::send_xcm(Here, project_multilocation, xcm) + .map_err(|_| Error::::MigrationFailed(MigrationError::XcmFailed))?; ActiveMigrationQueue::::insert(query_id, (project_id, participant.clone())); Self::deposit_event(Event::::MigrationStatusUpdated { @@ -1841,12 +1963,14 @@ impl Pallet { #[transactional] pub fn do_confirm_migrations(location: MultiLocation, query_id: QueryId, response: Response) -> DispatchResult { use xcm::v3::prelude::*; - let (project_id, participant) = ActiveMigrationQueue::::take(query_id)?; - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let (project_id, participant) = ActiveMigrationQueue::::take(query_id) + .ok_or(Error::::MigrationFailed(MigrationError::NoActiveMigrationsFound))?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; ensure!( matches!(location, MultiLocation { parents: 1, interior: X1(Parachain(para_id))} if Some(ParaId::from(para_id)) == project_details.parachain_id), - Error::::NotAllowed + Error::::MigrationFailed(MigrationError::WrongParaId) ); let status = match response { @@ -1948,7 +2072,7 @@ impl Pallet { // temp variable to store the total value of the bids (i.e price * amount = Cumulative Ticket Size) let mut bid_usd_value_sum = BalanceOf::::zero(); let project_account = Self::fund_account_id(project_id); - let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PLMCPriceNotAvailable)?; + let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PriceNotFound)?; // sort bids by price, and equal prices sorted by id bids.sort_by(|a, b| b.cmp(a)); @@ -2015,8 +2139,10 @@ impl Pallet { // lastly, sum all the weighted prices to get the final weighted price for the next funding round // 3 + 10.6 + 2.6 = 16.333... - let current_bucket = Buckets::::get(project_id).ok_or(Error::::ProjectNotFound)?; - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; + let current_bucket = + Buckets::::get(project_id).ok_or(Error::::ProjectError(ProjectErrorReason::BucketNotFound))?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; let is_first_bucket = current_bucket.current_price == project_metadata.minimum_price; let calc_weighted_price_fn = |bid: &BidInfoOf| -> PriceOf { @@ -2097,7 +2223,7 @@ impl Pallet { info.funding_amount_reached.saturating_accrue(final_total_funding_reached_by_bids); Ok(()) } else { - Err(Error::::ProjectNotFound.into()) + Err(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound).into()) } })?; @@ -2213,7 +2339,7 @@ impl Pallet { // We do think the UX would be bad if they cannot use all of their available tokens. // Specially since a new funding asset account can be easily created by increasing the provider reference T::FundingCurrency::transfer(asset_id, who, &fund_account, amount, Preservation::Expendable) - .map_err(|_| Error::::NotEnoughFunds)?; + .map_err(|_| Error::::ParticipationFailed(ParticipationError::NotEnoughFunds))?; Ok(()) } @@ -2260,8 +2386,10 @@ impl Pallet { /// reusable, not just for evaluator rewards. pub fn generate_evaluator_rewards_info(project_id: ProjectId) -> Result<(RewardInfoOf, u32), DispatchError> { // Fetching the necessary data for a specific project. - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectNotFound)?; - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; let evaluations = Evaluations::::iter_prefix((project_id,)).collect::>(); // used for weight calculation let evaluations_count = evaluations.len() as u32; @@ -2319,8 +2447,10 @@ impl Pallet { project_id: ProjectId, ) -> Result<(BalanceOf, BalanceOf), DispatchError> { // Fetching the necessary data for a specific project. - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectNotFound)?; - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; // Determine how much funding has been achieved. let funding_amount_reached = project_details.funding_amount_reached; @@ -2372,7 +2502,8 @@ impl Pallet { let insertion_iterations = match Self::add_to_update_store(now + settlement_delta, (&project_id, UpdateType::StartSettlement)) { Ok(iterations) => iterations, - Err(_iterations) => return Err(Error::::TooManyInsertionAttempts.into()), + Err(_iterations) => + return Err(Error::::ProjectRoundError(RoundError::TooManyInsertionAttempts).into()), }; Self::deposit_event(Event::ProjectPhaseTransition { project_id, @@ -2446,8 +2577,8 @@ impl Pallet { } fn change_migration_status(project_id: ProjectId, user: T::AccountId, status: MigrationStatus) -> DispatchResult { - let (current_status, migrations) = - UserMigrations::::get(project_id, user.clone()).ok_or(Error::::NoMigrationsFound)?; + let (current_status, migrations) = UserMigrations::::get(project_id, user.clone()) + .ok_or(Error::::MigrationFailed(MigrationError::NoMigrationsFound))?; let status = match status { MigrationStatus::Sent(_) if matches!(current_status, MigrationStatus::NotStarted | MigrationStatus::Failed) => diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 14d33698a..210710157 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -566,13 +566,7 @@ pub mod pallet { } #[pallet::storage] - pub type ActiveMigrationQueue = StorageMap< - _, - Blake2_128Concat, - QueryId, - (ProjectId, T::AccountId), - ResultQuery::NoActiveMigrationsFound>, - >; + pub type ActiveMigrationQueue = StorageMap<_, Blake2_128Concat, QueryId, (ProjectId, T::AccountId)>; /// A map to keep track of what issuer's did has an active project. It prevents one issuer having multiple active projects #[pallet::storage] @@ -706,113 +700,31 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// Something in storage has a state which should never be possible at this point. Programming error + /// Something in storage has a state which should never be possible at this point. Programming error. ImpossibleState, - /// The specified project does not exist - ProjectNotFound, - /// The Evaluation Round of the project has not started yet - EvaluationNotStarted, - /// The issuer cannot participate to their own project - ParticipationToThemselves, - /// Only the issuer can start the Evaluation Round + /// Action is not allowed. NotAllowed, - /// The Metadata Hash of the project was not found - MetadataNotProvided, - /// The Auction Round of the project has not started yet - AuctionNotStarted, - /// You cannot edit the metadata of a project that already passed the Evaluation Round - Frozen, - /// The bid is too low - BidTooLow, - /// Bid above the ticket size limit - BidTooHigh, - /// The Funding Round of the project has not ended yet - CannotClaimYet, - /// Tried to freeze the project to start the Evaluation Round, but the project is already frozen - ProjectAlreadyFrozen, - /// Tried to move the project from Application to Evaluation round, but the project is not in ApplicationRound - ProjectNotInApplicationRound, - /// Tried to move the project from Evaluation to EvaluationEnded round, but the project is not in EvaluationRound - ProjectNotInEvaluationRound, - /// Tried to move the project from AuctionInitializePeriod to AuctionOpeningRound, but the project is not in AuctionInitializePeriodRound - ProjectNotInAuctionInitializePeriodRound, - /// Tried to move the project to AuctionClosing, but it was not in AuctionOpeningRound before - ProjectNotInAuctionOpeningRound, - /// Tried to move the project to Community round, but it was not in AuctionClosingRound before - ProjectNotInAuctionClosingRound, - /// Tried to move the project to RemainderRound, but it was not in CommunityRound before - ProjectNotInCommunityRound, - /// Tried to move the project to ReadyToLaunch round, but it was not in FundingEnded round before - ProjectNotInFundingEndedRound, - /// Tried to start an auction before the initialization period - TooEarlyForAuctionOpeningStart, - /// Tried to move the project to AuctionClosingRound, but its too early for that - TooEarlyForAuctionClosingStart, - /// Tried to move the project to CommunityRound, but its too early for that - TooEarlyForCommunityRoundStart, - /// Tried to move the project to RemainderRound, but its too early for that - TooEarlyForRemainderRoundStart, - /// Tried to move to project to FundingEnded round, but its too early for that - TooEarlyForFundingEnd, - /// Checks for other projects not copying metadata of others - MetadataAlreadyExists, - /// The specified project details does not exist - ProjectDetailsNotFound, - /// Tried to finish an evaluation before its target end block - EvaluationPeriodNotEnded, - /// Tried to access field that is not set - FieldIsNone, - /// Checked math failed + /// Checked math failed. BadMath, - /// Tried to retrieve a evaluation, bid or contribution but it does not exist - ParticipationNotFound, - /// Tried to contribute but its too low to be accepted - ContributionTooLow, - /// Contribution is higher than the limit set by the issuer - ContributionTooHigh, - /// The provided asset is not accepted by the project issuer - FundingAssetNotAccepted, - /// Could not get the price in USD for PLMC - PLMCPriceNotAvailable, - /// Could not get the price in USD for the provided asset + /// Could not get the price in USD for an asset/PLMC. PriceNotFound, - /// Bond is either lower than the minimum set by the issuer, or the vec is full and can't replace an old one with a lower value - EvaluationBondTooLow, - /// Tried to do an operation on a finalizer that already finished - FinalizerFinished, - /// Tried to start a migration check but the bidirectional channel is not yet open - CommsNotEstablished, - XcmFailed, - // Tried to convert one type into another and failed. i.e try_into failed - BadConversion, - /// The issuer doesn't have enough funds (ExistentialDeposit), to create the escrow account - NotEnoughFundsForEscrowCreation, - /// Too many attempts to insert project in to ProjectsToUpdate storage - TooManyInsertionAttempts, - /// Reached bid limit for this user on this project - TooManyBidsForUser, - /// Reached bid limit for this project - TooManyBidsForProject, - /// Reached evaluation limit for this project - TooManyEvaluationsForProject, - /// Reached contribution limit for this user on this project - TooManyContributionsForUser, - /// Reached the migration queue limit for a user. - TooManyMigrations, - /// User has no migrations to execute. - NoMigrationsFound, - /// User has no active migrations in the queue. - NoActiveMigrationsFound, - // Participant tried to do a community contribution but it already had a winning bid on the auction round. - UserHasWinningBids, - // Round transition already happened. - RoundTransitionAlreadyHappened, - // User tried to use a multiplier not allowed - ForbiddenMultiplier, - /// The issuer tried to create a new project but already has an active one - IssuerHasActiveProjectAlready, - NotEnoughFunds, + /// Tried to retrieve a evaluation, bid or contribution but it does not exist. + ParticipationNotFound, + /// The user has the incorrect investor type for the action. + WrongInvestorType, + /// Project Error. Project information not found, or project has an incorrect state. + ProjectError(ProjectErrorReason), + /// A round related error. The project did not have the correct state to execute the action. + ProjectRoundError(RoundError), + /// Issuer related error. E.g. the action was not executed by the issuer, or the issuer + /// did not have the correct state to execute an action. + IssuerError(IssuerErrorReason), + /// The project's metadata is incorrect. BadMetadata(MetadataError), + /// Error related to an participation action. Evaluation, bid or contribution failed. + ParticipationFailed(ParticipationError), + /// A error related to the migration process. + MigrationFailed(MigrationError), } #[pallet::call] @@ -827,7 +739,7 @@ pub mod pallet { ) -> DispatchResult { let (account, did, investor_type) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); + ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_create_project(&account, project, did) } @@ -836,7 +748,7 @@ pub mod pallet { pub fn remove_project(origin: OriginFor, jwt: UntrustedToken, project_id: ProjectId) -> DispatchResult { let (account, did, investor_type) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); + ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_remove_project(account, project_id, did) } @@ -851,7 +763,7 @@ pub mod pallet { ) -> DispatchResult { let (account, _did, investor_type) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); + ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_edit_project(account, project_id, new_project_metadata) } @@ -865,7 +777,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let (account, _did, investor_type) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); + ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_start_evaluation(account, project_id) } @@ -881,7 +793,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let (account, _did, investor_type) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); + ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_auction_opening(account, project_id) } @@ -985,7 +897,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let (account, _did, investor_type) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); + ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_decide_project_outcome(account, project_id, outcome) } @@ -1082,7 +994,7 @@ pub mod pallet { ) -> DispatchResult { let (account, _did, investor_type) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); + ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_set_para_id_for_project(&account, project_id, para_id) } @@ -1096,7 +1008,7 @@ pub mod pallet { ) -> DispatchResult { let (account, _did, investor_type) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - ensure!(investor_type == InvestorType::Institutional, Error::::NotAllowed); + ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_start_migration_readiness_check(&account, project_id) } diff --git a/pallets/funding/src/settlement.rs b/pallets/funding/src/settlement.rs index 4d9bf6298..53068c0c0 100644 --- a/pallets/funding/src/settlement.rs +++ b/pallets/funding/src/settlement.rs @@ -22,8 +22,12 @@ use sp_runtime::{ impl Pallet { pub fn do_settle_successful_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::::NotAllowed); + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + ensure!( + matches!(project_details.status, ProjectStatus::FundingSuccessful), + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); // Based on the results of the funding round, the evaluator is either: // 1. Slashed @@ -71,8 +75,12 @@ impl Pallet { } pub fn do_settle_failed_evaluation(evaluation: EvaluationInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::::NotAllowed); + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + ensure!( + matches!(project_details.status, ProjectStatus::FundingFailed), + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); let bond = if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) { Self::slash_evaluator(project_id, &evaluation)? @@ -102,11 +110,21 @@ impl Pallet { } pub fn do_settle_successful_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; - ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); - ensure!(matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), Error::::NotAllowed); - ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); + ensure!( + project_details.status == ProjectStatus::FundingSuccessful, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); + ensure!( + matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)), + Error::::ImpossibleState + ); + ensure!( + T::ContributionTokenCurrency::asset_exists(project_id), + Error::::ProjectRoundError(RoundError::TooEarlyForRound) + ); let bidder = bid.bidder; @@ -162,8 +180,12 @@ impl Pallet { } pub fn do_settle_failed_bid(bid: BidInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::::NotAllowed); + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + ensure!( + matches!(project_details.status, ProjectStatus::FundingFailed), + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); let bidder = bid.bidder; @@ -185,12 +207,19 @@ impl Pallet { contribution: ContributionInfoOf, project_id: ProjectId, ) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; // Ensure that: // 1. The project is in the FundingSuccessful state // 2. The contribution token exists - ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::::NotAllowed); - ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::::CannotClaimYet); + ensure!( + project_details.status == ProjectStatus::FundingSuccessful, + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); + ensure!( + T::ContributionTokenCurrency::asset_exists(project_id), + Error::::ProjectRoundError(RoundError::TooEarlyForRound) + ); let contributor = contribution.contributor; @@ -246,8 +275,12 @@ impl Pallet { } pub fn do_settle_failed_contribution(contribution: ContributionInfoOf, project_id: ProjectId) -> DispatchResult { - let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::::NotAllowed); + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; + ensure!( + matches!(project_details.status, ProjectStatus::FundingFailed), + Error::::ProjectRoundError(RoundError::IncorrectRound) + ); // Check if the bidder has a future deposit held let contributor = contribution.contributor; @@ -379,10 +412,14 @@ impl Pallet { let migration_info: MigrationInfo = (ct_amount.into(), vesting_time.into()).into(); let migration = Migration::new(migration_origin, migration_info); if let Some((_, migrations)) = maybe_migrations { - migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; + migrations + .try_push(migration) + .map_err(|_| Error::::MigrationFailed(MigrationError::TooManyMigrations))?; } else { let mut migrations = BoundedVec::<_, MaxParticipationsPerUser>::new(); - migrations.try_push(migration).map_err(|_| Error::::TooManyMigrations)?; + migrations + .try_push(migration) + .map_err(|_| Error::::MigrationFailed(MigrationError::TooManyMigrations))?; *maybe_migrations = Some((MigrationStatus::NotStarted, migrations)) } diff --git a/pallets/funding/src/tests/1_application.rs b/pallets/funding/src/tests/1_application.rs index d0a66c9dc..de5d1ab6c 100644 --- a/pallets/funding/src/tests/1_application.rs +++ b/pallets/funding/src/tests/1_application.rs @@ -139,7 +139,7 @@ mod create_project_extrinsic { jwt.clone(), project_metadata.clone() ), - Error::::IssuerHasActiveProjectAlready + Error::::IssuerError(IssuerErrorReason::HasActiveProject) ); }); @@ -152,7 +152,7 @@ mod create_project_extrinsic { jwt.clone(), project_metadata.clone() ), - Error::::IssuerHasActiveProjectAlready + Error::::IssuerError(IssuerErrorReason::HasActiveProject) ); }); inst.advance_time(::EvaluationDuration::get() + 1).unwrap(); @@ -178,7 +178,7 @@ mod create_project_extrinsic { jwt.clone(), project_metadata.clone() ), - Error::::IssuerHasActiveProjectAlready + Error::::IssuerError(IssuerErrorReason::HasActiveProject) ); }); inst.finish_funding(1).unwrap(); @@ -251,7 +251,7 @@ mod create_project_extrinsic { jwt, project_metadata.clone() ), - Error::::NotAllowed + Error::::WrongInvestorType ); }); @@ -263,7 +263,7 @@ mod create_project_extrinsic { jwt, project_metadata ), - Error::::NotAllowed + Error::::WrongInvestorType ); }); } @@ -294,7 +294,7 @@ mod create_project_extrinsic { jwt.clone(), project_metadata.clone() ), - Error::::IssuerHasActiveProjectAlready + Error::::IssuerError(IssuerErrorReason::HasActiveProject) ); }); } @@ -309,7 +309,7 @@ mod create_project_extrinsic { inst.execute(|| { assert_noop!( Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata,), - Error::::NotEnoughFundsForEscrowCreation + Error::::IssuerError(IssuerErrorReason::NotEnoughFunds) ); }); } @@ -691,7 +691,7 @@ mod edit_project_extrinsic { project_id_1, project_metadata_2 ), - Error::::NotAllowed + Error::::IssuerError(IssuerErrorReason::NotIssuer) ); assert_noop!( Pallet::::edit_project( @@ -700,7 +700,7 @@ mod edit_project_extrinsic { project_id_2, project_metadata_1 ), - Error::::NotAllowed + Error::::IssuerError(IssuerErrorReason::NotIssuer) ); }); } @@ -721,7 +721,7 @@ mod edit_project_extrinsic { project_id, project_metadata.clone() ), - Error::::Frozen + Error::::ProjectError(ProjectErrorReason::ProjectIsFrozen) ); }); } @@ -743,7 +743,7 @@ mod edit_project_extrinsic { project_id, project_metadata.clone() ), - Error::::NotAllowed + Error::::WrongInvestorType ); }); @@ -756,7 +756,7 @@ mod edit_project_extrinsic { project_id, project_metadata ), - Error::::NotAllowed + Error::::WrongInvestorType ); }); } @@ -817,7 +817,7 @@ mod remove_project_extrinsic { jwt.clone(), project_metadata.clone() ), - Error::::IssuerHasActiveProjectAlready + Error::::IssuerError(IssuerErrorReason::HasActiveProject) ); }); @@ -855,7 +855,7 @@ mod remove_project_extrinsic { jwt.clone(), project_id ), - Error::::NotAllowed + Error::::WrongInvestorType ); }); } @@ -876,7 +876,7 @@ mod remove_project_extrinsic { jwt.clone(), project_id ), - Error::::NotAllowed + Error::::IssuerError(IssuerErrorReason::NotIssuer) ); }); } @@ -896,7 +896,7 @@ mod remove_project_extrinsic { jwt.clone(), project_id, ), - Error::::Frozen + Error::::ProjectError(ProjectErrorReason::ProjectIsFrozen) ); }); } diff --git a/pallets/funding/src/tests/2_evaluation.rs b/pallets/funding/src/tests/2_evaluation.rs index 37539869c..1e9050bee 100644 --- a/pallets/funding/src/tests/2_evaluation.rs +++ b/pallets/funding/src/tests/2_evaluation.rs @@ -227,7 +227,7 @@ mod start_evaluation_extrinsic { get_mock_jwt(issuer, InvestorType::Professional, generate_did_from_account(issuer)), project_id ), - Error::::NotAllowed + Error::::WrongInvestorType ); }); @@ -238,7 +238,7 @@ mod start_evaluation_extrinsic { get_mock_jwt(issuer, InvestorType::Retail, generate_did_from_account(issuer)), project_id ), - Error::::NotAllowed + Error::::WrongInvestorType ); }); } @@ -262,7 +262,7 @@ mod start_evaluation_extrinsic { inst.execute(|| { assert_noop!( PolimecFunding::start_evaluation(RuntimeOrigin::signed(issuer), jwt, project_id), - Error::::ProjectNotInApplicationRound + Error::::ProjectRoundError(RoundError::IncorrectRound) ); }); } @@ -280,7 +280,7 @@ mod start_evaluation_extrinsic { inst.execute(|| { assert_noop!( PolimecFunding::start_evaluation(RuntimeOrigin::signed(issuer), jwt, project_id), - Error::::MetadataNotProvided + Error::::BadMetadata(MetadataError::MetadataNotProvided) ); }); } @@ -307,7 +307,7 @@ mod start_evaluation_extrinsic { get_mock_jwt(ISSUER_2, InvestorType::Institutional, generate_did_from_account(ISSUER_2)), project_id ), - Error::::NotAllowed + Error::::IssuerError(IssuerErrorReason::NotIssuer) ); }); } @@ -459,7 +459,7 @@ mod evaluate_extrinsic { project_id, 500 * US_DOLLAR, ), - Error::::ProjectNotInEvaluationRound + Error::::ProjectRoundError(RoundError::IncorrectRound) ); }); } @@ -535,7 +535,7 @@ mod evaluate_extrinsic { assert_err!( inst.evaluate_for_users(project_id, vec![failing_evaluation]), - Error::::TooManyEvaluationsForProject + Error::::ParticipationFailed(ParticipationError::TooManyProjectParticipations) ); } @@ -592,7 +592,7 @@ mod evaluate_extrinsic { generate_did_from_account(ISSUER_1), InvestorType::Institutional )), - Error::::ParticipationToThemselves + Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); } diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index 510134816..e57a97ccb 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -7,8 +7,7 @@ mod round_flow { #[cfg(test)] mod success { use super::*; - use sp_arithmetic::PerThing; - use sp_core::{bounded_vec, ConstU32}; + use sp_core::bounded_vec; use std::ops::Not; #[test] @@ -327,7 +326,6 @@ mod round_flow { .unwrap(); inst.bid_for_users(project_id, vec![after_random_end_bid]).unwrap(); - let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); inst.do_free_plmc_assertions(vec![ UserToPLMCBalance::new(BIDDER_1, MockInstantiator::get_ed()), UserToPLMCBalance::new(BIDDER_2, MockInstantiator::get_ed()), @@ -357,7 +355,6 @@ mod round_flow { vec![plmc_returned.clone(), plmc_existential_amounts, vec![rejected_bid_necessary_plmc.clone()]], MergeOperation::Add, ); - let stored_bids = inst.execute(|| Bids::::iter_prefix_values((project_id,)).collect_vec()); inst.do_free_plmc_assertions(expected_free); let expected_reserved = MockInstantiator::generic_map_operation( vec![necessary_plmc.clone(), plmc_returned.clone(), vec![rejected_bid_necessary_plmc.clone()]], @@ -527,7 +524,7 @@ mod round_flow { did, investor_type ), - Error::::AuctionNotStarted + Error::::ProjectRoundError(RoundError::IncorrectRound) ); }); } @@ -593,7 +590,7 @@ mod start_auction_extrinsic { for account in 6000..6010 { inst.execute(|| { let response = Pallet::::do_auction_opening(account, project_id); - assert_noop!(response, Error::::NotAllowed); + assert_noop!(response, Error::::IssuerError(IssuerErrorReason::NotIssuer)); }); } } @@ -610,7 +607,7 @@ mod start_auction_extrinsic { inst.execute(|| { assert_noop!( PolimecFunding::do_auction_opening(ISSUER_1, project_id), - Error::::EvaluationPeriodNotEnded + Error::::ProjectRoundError(RoundError::TransitionPointNotSet) ); }); } @@ -623,7 +620,7 @@ mod start_auction_extrinsic { inst.execute(|| { assert_noop!( PolimecFunding::do_auction_opening(ISSUER_1, project_id), - Error::::EvaluationPeriodNotEnded + Error::::ProjectRoundError(RoundError::TransitionPointNotSet) ); }); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); @@ -662,7 +659,6 @@ mod bid_extrinsic { mod success { use super::*; use frame_support::dispatch::DispatchResultWithPostInfo; - use polimec_common::credentials::Empty; #[test] fn evaluation_bond_counts_towards_bid() { @@ -821,30 +817,30 @@ mod bid_extrinsic { 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 + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.bid_for_users(project_id_usdt, vec![dot_bid.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.bid_for_users(project_id_usdc, vec![usdt_bid.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::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 + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.bid_for_users(project_id_dot, vec![usdt_bid.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.bid_for_users(project_id_dot, vec![usdc_bid.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_ok!(inst.bid_for_users(project_id_dot, vec![dot_bid.clone()])); } @@ -907,7 +903,7 @@ mod bid_extrinsic { // Professional bids: 0x multiplier should fail assert_err!( test_bid_setup(&mut inst, project_id, BIDDER_1, InvestorType::Professional, 0), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); // Professional bids: 1 - 10x multiplier should work for multiplier in 1..=10u8 { @@ -917,14 +913,14 @@ mod bid_extrinsic { for multiplier in 11..=50u8 { assert_err!( test_bid_setup(&mut inst, project_id, BIDDER_1, InvestorType::Professional, multiplier), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); } // Institutional bids: 0x multiplier should fail assert_err!( test_bid_setup(&mut inst, project_id, BIDDER_2, InvestorType::Institutional, 0), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); // Institutional bids: 1 - 25x multiplier should work for multiplier in 1..=25u8 { @@ -934,7 +930,7 @@ mod bid_extrinsic { for multiplier in 26..=50u8 { assert_err!( test_bid_setup(&mut inst, project_id, BIDDER_2, InvestorType::Institutional, multiplier), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); } } @@ -1072,7 +1068,7 @@ mod bid_extrinsic { did, investor_type ), - Error::::AuctionNotStarted + Error::::ProjectRoundError(RoundError::IncorrectRound) ); }); } @@ -1152,7 +1148,7 @@ mod bid_extrinsic { failing_bid.multiplier, failing_bid.asset ), - Error::::TooManyBidsForProject + Error::::ParticipationFailed(ParticipationError::TooManyProjectParticipations) ); }); @@ -1185,7 +1181,7 @@ mod bid_extrinsic { failing_bid.multiplier, failing_bid.asset ), - Error::::TooManyBidsForProject + Error::::ParticipationFailed(ParticipationError::TooManyProjectParticipations) ); }); } @@ -1203,7 +1199,7 @@ mod bid_extrinsic { vec![100u8], ); let max_bids_per_user: u32 = ::MaxBidsPerUser::get(); - let bids = (0u32..max_bids_per_user - 1u32).map(|i| (BIDDER_1, 5000 * ASSET_UNIT).into()).collect_vec(); + let bids = (0u32..max_bids_per_user - 1u32).map(|_| (BIDDER_1, 5000 * ASSET_UNIT).into()).collect_vec(); let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, evaluations); @@ -1263,7 +1259,7 @@ mod bid_extrinsic { failing_bid.multiplier, failing_bid.asset ), - Error::::TooManyBidsForUser + Error::::ParticipationFailed(ParticipationError::TooManyUserParticipations) ); }); @@ -1296,7 +1292,7 @@ mod bid_extrinsic { failing_bid.multiplier, failing_bid.asset ), - Error::::TooManyBidsForUser + Error::::ParticipationFailed(ParticipationError::TooManyUserParticipations) ); }); } @@ -1335,7 +1331,7 @@ mod bid_extrinsic { generate_did_from_account(BIDDER_1), InvestorType::Professional ), - Error::::BidTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); }); // bid below 2000 CT (20k USD) should fail for institutionals @@ -1350,7 +1346,7 @@ mod bid_extrinsic { generate_did_from_account(BIDDER_1), InvestorType::Institutional ), - Error::::BidTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); }); } @@ -1482,7 +1478,7 @@ mod bid_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT ), - Error::::BidTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); }); // bidding 10k total works @@ -1521,7 +1517,7 @@ mod bid_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::BidTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); }); // bidding 50k total works @@ -1552,7 +1548,7 @@ mod bid_extrinsic { generate_did_from_account(ISSUER_1), InvestorType::Institutional )), - Error::::ParticipationToThemselves + Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); } @@ -1577,7 +1573,10 @@ mod bid_extrinsic { investor_type, ) }); - frame_support::assert_err!(outcome, Error::::FundingAssetNotAccepted); + frame_support::assert_err!( + outcome, + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) + ); } } } diff --git a/pallets/funding/src/tests/4_community.rs b/pallets/funding/src/tests/4_community.rs index a75369742..2322e6a25 100644 --- a/pallets/funding/src/tests/4_community.rs +++ b/pallets/funding/src/tests/4_community.rs @@ -515,7 +515,7 @@ fn per_credential_type_ticket_size_minimums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); }); // contribution below 10_000 CT (100k USD) should fail for professionals @@ -530,7 +530,7 @@ fn per_credential_type_ticket_size_minimums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); }); @@ -547,7 +547,7 @@ fn per_credential_type_ticket_size_minimums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); }); } @@ -625,7 +625,7 @@ fn per_credential_type_ticket_size_maximums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); }); // bidding 2k total works @@ -663,7 +663,7 @@ fn per_credential_type_ticket_size_maximums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); }); // bidding 2k total works @@ -701,7 +701,7 @@ fn per_credential_type_ticket_size_maximums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); }); // bidding 5k total works @@ -838,30 +838,30 @@ fn contribute_with_multiple_currencies() { 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 + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.contribute_for_users(project_id_usdt, vec![dot_contribution.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.contribute_for_users(project_id_usdc, vec![usdt_contribution.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::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 + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.contribute_for_users(project_id_dot, vec![usdt_contribution.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.contribute_for_users(project_id_dot, vec![usdc_contribution.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_ok!(inst.contribute_for_users(project_id_dot, vec![dot_contribution.clone()])); } @@ -886,7 +886,7 @@ fn issuer_cannot_contribute_his_project() { generate_did_from_account(ISSUER_1), InvestorType::Institutional )), - Error::::ParticipationToThemselves + Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); } @@ -921,7 +921,7 @@ fn did_with_winning_bid_cannot_contribute() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::UserHasWinningBids + Error::::ParticipationFailed(ParticipationError::UserHasWinningBid) ); }); @@ -935,7 +935,7 @@ fn did_with_winning_bid_cannot_contribute() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::UserHasWinningBids + Error::::ParticipationFailed(ParticipationError::UserHasWinningBid) ); }); @@ -1002,7 +1002,7 @@ fn non_retail_multiplier_limits() { Multiplier::force_new(0), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); // Professional bids: 1 - 10x multiplier should work @@ -1054,7 +1054,7 @@ fn non_retail_multiplier_limits() { Multiplier::force_new(multiplier), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); } @@ -1071,7 +1071,7 @@ fn non_retail_multiplier_limits() { Multiplier::force_new(0), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); // Institutional bids: 1 - 25x multiplier should work @@ -1123,7 +1123,7 @@ fn non_retail_multiplier_limits() { Multiplier::force_new(multiplier), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); } @@ -1204,7 +1204,7 @@ fn retail_multiplier_limits() { Multiplier::force_new(0), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); @@ -1218,7 +1218,10 @@ fn retail_multiplier_limits() { // Multipliers that should NOT work for multiplier in max_allowed_multiplier + 1..=50 { log::debug!("error? - multiplier: {}", multiplier); - assert_err!(contribute(&mut inst, project_id, multiplier), Error::::ForbiddenMultiplier); + assert_err!( + contribute(&mut inst, project_id, multiplier), + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) + ); } } } diff --git a/pallets/funding/src/tests/5_remainder.rs b/pallets/funding/src/tests/5_remainder.rs index d9f0fd9f1..6012376da 100644 --- a/pallets/funding/src/tests/5_remainder.rs +++ b/pallets/funding/src/tests/5_remainder.rs @@ -263,7 +263,7 @@ fn per_credential_type_ticket_size_minimums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); }); // contribution below 10_000 CT (100k USD) should fail for professionals @@ -278,7 +278,7 @@ fn per_credential_type_ticket_size_minimums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); }); @@ -294,7 +294,7 @@ fn per_credential_type_ticket_size_minimums() { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, ), - Error::::ContributionTooLow + Error::::ParticipationFailed(ParticipationError::TooLow) ); }); } @@ -374,7 +374,7 @@ fn per_credential_type_ticket_size_maximums() { generate_did_from_account(BUYER_4), InvestorType::Retail ), - Error::::ContributionTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); }); // bidding 2k total works @@ -415,7 +415,7 @@ fn per_credential_type_ticket_size_maximums() { generate_did_from_account(BUYER_6), InvestorType::Professional ), - Error::::ContributionTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); }); // bidding 2k total works @@ -456,7 +456,7 @@ fn per_credential_type_ticket_size_maximums() { generate_did_from_account(BUYER_8), InvestorType::Institutional ), - Error::::ContributionTooHigh + Error::::ParticipationFailed(ParticipationError::TooHigh) ); }); // bidding 5k total works @@ -595,30 +595,30 @@ fn contribute_with_multiple_currencies() { 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 + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.contribute_for_users(project_id_usdt, vec![dot_contribution.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.contribute_for_users(project_id_usdc, vec![usdt_contribution.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::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 + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.contribute_for_users(project_id_dot, vec![usdt_contribution.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_err!( inst.contribute_for_users(project_id_dot, vec![usdc_contribution.clone()]), - Error::::FundingAssetNotAccepted + Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); assert_ok!(inst.contribute_for_users(project_id_dot, vec![dot_contribution.clone()])); } @@ -644,7 +644,7 @@ fn issuer_cannot_contribute_his_project() { generate_did_from_account(ISSUER_1), InvestorType::Institutional )), - Error::::ParticipationToThemselves + Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); } @@ -700,7 +700,7 @@ fn non_retail_multiplier_limits() { Multiplier::force_new(0), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); // Professional bids: 1 - 10x multiplier should work @@ -752,7 +752,7 @@ fn non_retail_multiplier_limits() { Multiplier::force_new(multiplier), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); } @@ -769,7 +769,7 @@ fn non_retail_multiplier_limits() { Multiplier::force_new(0), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); // Institutional bids: 1 - 25x multiplier should work @@ -821,7 +821,7 @@ fn non_retail_multiplier_limits() { Multiplier::force_new(multiplier), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); } @@ -903,7 +903,7 @@ fn retail_multiplier_limits() { Multiplier::force_new(0), AcceptedFundingAsset::USDT ), - Error::::ForbiddenMultiplier + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) ); }); @@ -916,7 +916,10 @@ fn retail_multiplier_limits() { // Multipliers that should NOT work for multiplier in max_allowed_multiplier + 1..=50 { log::debug!("error? - multiplier: {}", multiplier); - assert_err!(contribute(&mut inst, project_id, multiplier), Error::::ForbiddenMultiplier); + assert_err!( + contribute(&mut inst, project_id, multiplier), + Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) + ); } } } diff --git a/pallets/funding/src/tests/8_ct_migration.rs b/pallets/funding/src/tests/8_ct_migration.rs index 7e515e7f2..af1cb8b0e 100644 --- a/pallets/funding/src/tests/8_ct_migration.rs +++ b/pallets/funding/src/tests/8_ct_migration.rs @@ -41,15 +41,15 @@ fn para_id_for_project_cannot_be_set_by_anyone_but_issuer() { inst.execute(|| { assert_err!( crate::Pallet::::do_set_para_id_for_project(&EVALUATOR_1, project_id, ParaId::from(2006u32),), - Error::::NotAllowed + Error::::IssuerError(IssuerErrorReason::NotIssuer) ); assert_err!( crate::Pallet::::do_set_para_id_for_project(&BIDDER_1, project_id, ParaId::from(2006u32),), - Error::::NotAllowed + Error::::IssuerError(IssuerErrorReason::NotIssuer) ); assert_err!( crate::Pallet::::do_set_para_id_for_project(&BUYER_1, project_id, ParaId::from(2006u32),), - Error::::NotAllowed + Error::::IssuerError(IssuerErrorReason::NotIssuer) ); }); let project_details = inst.get_project_details(project_id); diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index f02c0d4a7..da6114525 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -210,9 +210,9 @@ pub mod storage_types { /// - Minimum price is not zero /// - Minimum bidding ticket sizes are higher than 5k USD /// - Specified participation currencies are unique - pub fn is_valid(&self) -> Result<(), ValidityError> { + pub fn is_valid(&self) -> Result<(), MetadataError> { if self.minimum_price == Price::zero() { - return Err(ValidityError::PriceTooLow); + return Err(MetadataError::PriceTooLow); } let min_bidder_bound_usd: Balance = (5000 * (US_DOLLAR as u64)).into(); self.bidding_ticket_sizes.is_valid(vec![ @@ -222,27 +222,27 @@ pub mod storage_types { self.contributing_ticket_sizes.is_valid(vec![])?; if self.total_allocation_size > self.mainnet_token_max_supply { - return Err(ValidityError::AllocationSizeError); + return Err(MetadataError::AllocationSizeError); } if self.total_allocation_size <= 0u64.into() { - return Err(ValidityError::AllocationSizeError); + return Err(MetadataError::AllocationSizeError); } if self.auction_round_allocation_percentage <= Percent::from_percent(0) { - return Err(ValidityError::AuctionRoundPercentageError); + return Err(MetadataError::AuctionRoundPercentageError); } let mut deduped = self.participation_currencies.clone().to_vec(); deduped.sort(); deduped.dedup(); if deduped.len() != self.participation_currencies.len() { - return Err(ValidityError::ParticipationCurrenciesError); + return Err(MetadataError::ParticipationCurrenciesError); } let target_funding = self.minimum_price.saturating_mul_int(self.total_allocation_size); if target_funding < (1000u64 * US_DOLLAR as u64).into() { - return Err(ValidityError::FundingTargetTooLow); + return Err(MetadataError::FundingTargetTooLow); } Ok(()) } @@ -508,16 +508,16 @@ pub mod inner_types { pub phantom: PhantomData<(Price, Balance)>, } impl BiddingTicketSizes { - pub fn is_valid(&self, usd_bounds: Vec>) -> Result<(), ValidityError> { + pub fn is_valid(&self, usd_bounds: Vec>) -> Result<(), MetadataError> { for bound in usd_bounds { match bound { InvestorTypeUSDBounds::Professional(bound) => if !self.professional.check_valid(bound) { - return Err(ValidityError::TicketSizeError); + return Err(MetadataError::TicketSizeError); }, InvestorTypeUSDBounds::Institutional(bound) => if !self.institutional.check_valid(bound) { - return Err(ValidityError::TicketSizeError); + return Err(MetadataError::TicketSizeError); }, _ => {}, } @@ -535,20 +535,20 @@ pub mod inner_types { pub phantom: PhantomData<(Price, Balance)>, } impl ContributingTicketSizes { - pub fn is_valid(&self, usd_bounds: Vec>) -> Result<(), ValidityError> { + pub fn is_valid(&self, usd_bounds: Vec>) -> Result<(), MetadataError> { for bound in usd_bounds { match bound { InvestorTypeUSDBounds::Professional(bound) => if !self.professional.check_valid(bound) { - return Err(ValidityError::TicketSizeError); + return Err(MetadataError::TicketSizeError); }, InvestorTypeUSDBounds::Institutional(bound) => if !self.institutional.check_valid(bound) { - return Err(ValidityError::TicketSizeError); + return Err(MetadataError::TicketSizeError); }, InvestorTypeUSDBounds::Retail(bound) => if !self.retail.check_valid(bound) { - return Err(ValidityError::TicketSizeError); + return Err(MetadataError::TicketSizeError); }, } } @@ -657,24 +657,125 @@ pub mod inner_types { } } - #[derive(Debug)] - pub enum ValidityError { + /// Errors related to round transitions and round state. + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, PalletError)] + pub enum RoundError { + /// The project is not in the correct round to execute the action. + IncorrectRound, + /// Too early to execute the action. The action can likely be called again at a later stage. + TooEarlyForRound, + /// A round transition was already executed, so the transition cannot be + /// executed again. This is likely to happen when the issuer manually transitions the project, + /// after which the automatic transition is executed. + RoundTransitionAlreadyHappened, + /// A project's transition point (block number) was not set. + TransitionPointNotSet, + /// Too many insertion attempts were made while inserting a project's round transition + /// in the `ProjectsToUpdate` storage. This should not happen in practice. + TooManyInsertionAttempts, + } + + /// Errors related to the participation actions. This can either be evaluate, bid or + /// contribute. If any of these errors are thrown, the participation failed. + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, PalletError)] + pub enum ParticipationError { + /// The participation amount is too low. + TooLow, + /// The participation amount is too high. + TooHigh, + /// The funding asset is not accepted for this project. + FundingAssetNotAccepted, + /// The user has too many participations in this project. + TooManyUserParticipations, + /// The project has too many participations. + TooManyProjectParticipations, + /// The user is not allowed to use this multiplier. + ForbiddenMultiplier, + /// The user has a winning bid in the auction round and is not allowed to participate + /// in the community round. + UserHasWinningBid, + /// The user does not have enough funds (funding asset or PLMC) to cover the participation. + NotEnoughFunds, + } + + /// Errors related to the project state. This can either be project info not found, or + /// incorrect project state. + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, PalletError)] + pub enum ProjectErrorReason { + /// The project details were not found. Happens when the project with provided ID does + /// not exist in the `ProjectsDetails` storage. + ProjectDetailsNotFound, + /// The project metadata was not found. Happens when the project with provided ID does + /// not exist in the `ProjectsMetadata` storage. + ProjectMetadataNotFound, + /// The project's bucket info was not found. Happens when the project with provided ID does + /// not exist in the `Buckets` storage. + BucketNotFound, + /// The project is already frozen, so cannot be frozen again. Happens when + /// `do_start_evaluation` is called on a project that has already started the + /// evaluation round. + ProjectAlreadyFrozen, + /// The project is frozen, so no changes to the metadata are allowed and the project + /// cannot be deleted anymore. + ProjectIsFrozen, + /// The project's weighted average price is not set while in the community round. + /// Should not happen in practice. + WapNotSet, + } + + /// Errors related to the issuer actions. + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, PalletError)] + pub enum IssuerErrorReason { + /// The action's caller is not the issuer of the project and is not allowed to execute + /// this action. + NotIssuer, + /// The issuer already has an active project. The issuer can only have one active project. + HasActiveProject, + /// The issuer tries to participate to their own project. + ParticipationToOwnProject, + /// The issuer has not enough funds to cover the escrow account costs. + NotEnoughFunds, + } + + /// Errors related to the project's metadata. + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, PalletError)] + pub enum MetadataError { + /// The minimum price per token is too low. PriceTooLow, + /// The ticket sizes are not valid. TicketSizeError, + /// The participation currencies are not unique. ParticipationCurrenciesError, + /// The allocation size is invalid. Either zero or higher than the max supply. AllocationSizeError, + /// The auction round percentage cannot be zero. AuctionRoundPercentageError, + /// The funding target has to be higher then 1000 USD. FundingTargetTooLow, + /// The project's metadata hash is not provided while starting the evaluation round. + MetadataNotProvided, } + /// Errors related to the project's migration process. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, PalletError)] - pub enum MetadataError { - PriceTooLow, - TicketSizeError, - ParticipationCurrenciesError, - AllocationSizeError, - AuctionRoundPercentageError, - FundingTargetTooLow, + pub enum MigrationError { + /// Tried to start a migration check but the bidirectional channel is not yet open + ChannelNotOpen, + /// The xcm execution/sending failed. + XcmFailed, + /// Reached limit on maximum number of migrations. In practise this should not happen, + /// as the max migrations is set to the sum of max evaluations, bids and contributions. + TooManyMigrations, + /// User has no migrations to execute. + NoMigrationsFound, + /// User has no active migrations in the queue. + NoActiveMigrationsFound, + /// Wrong para_id is provided. + WrongParaId, + /// Migration channel is not ready for migrations. + ChannelNotReady, + /// User still has participations that need to be settled before migration. + ParticipationsNotSettled, } #[derive(Default, Clone, Copy, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]