diff --git a/pallets/funding/src/functions/4_contribution.rs b/pallets/funding/src/functions/4_contribution.rs index 381b232bc..99a1c9912 100644 --- a/pallets/funding/src/functions/4_contribution.rs +++ b/pallets/funding/src/functions/4_contribution.rs @@ -38,6 +38,9 @@ impl Pallet { ensure!(now < round_end, Error::::TooLateForRound); let buyable_tokens = token_amount.min(project_details.remaining_contribution_tokens); + if buyable_tokens.is_zero() { + return Err(Error::::ProjectSoldOut.into()); + } project_details.remaining_contribution_tokens.saturating_reduce(buyable_tokens); let perform_params = DoPerformContributionParams { diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 706e46114..bf9fe2137 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -764,6 +764,8 @@ pub mod pallet { ParticipantNotEnoughFunds, /// The JWT included the wrong policy for participating in this project. PolicyMismatch, + /// Contribution tokens have all been sold + ProjectSoldOut, // * An error related to the migration process. * /// Tried to start a migration check but the bidirectional channel is not yet open diff --git a/pallets/funding/src/tests/4_contribution.rs b/pallets/funding/src/tests/4_contribution.rs index 70248e246..275a526ef 100644 --- a/pallets/funding/src/tests/4_contribution.rs +++ b/pallets/funding/src/tests/4_contribution.rs @@ -1741,5 +1741,51 @@ mod contribute_extrinsic { ); }); } + + #[test] + fn ct_sold_out() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_community_contributing_project( + project_metadata.clone(), + ISSUER_1, + None, + default_evaluations(), + default_bids(), + ); + let project_details = inst.get_project_details(project_id); + let remaining_cts = project_details.remaining_contribution_tokens; + let glutton_contribution = ContributionParams::new(BUYER_1, remaining_cts, 4u8, AcceptedFundingAsset::USDT); + let wap = project_details.weighted_average_price.unwrap(); + let plmc_mint = inst.calculate_contributed_plmc_spent(vec![glutton_contribution.clone()], wap, true); + let funding_asset_mint = inst.calculate_contributed_funding_asset_spent(vec![glutton_contribution.clone()], wap); + inst.mint_plmc_to(plmc_mint); + inst.mint_funding_asset_to(funding_asset_mint); + inst.contribute_for_users(project_id, vec![glutton_contribution.clone()]).unwrap(); + + let failing_contribution = ContributionParams::::new(BUYER_2, 1000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT); + let plmc_mint = inst.calculate_contributed_plmc_spent(vec![glutton_contribution.clone()], wap, true); + let funding_asset_mint = inst.calculate_contributed_funding_asset_spent(vec![glutton_contribution.clone()], wap); + inst.mint_plmc_to(plmc_mint); + inst.mint_funding_asset_to(funding_asset_mint); + inst.execute(|| { + assert_noop!( + PolimecFunding::contribute( + RuntimeOrigin::signed(failing_contribution.contributor), + get_mock_jwt_with_cid( + failing_contribution.contributor, + InvestorType::Retail, + generate_did_from_account(failing_contribution.contributor), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), + project_id, + failing_contribution.amount, + failing_contribution.multiplier, + failing_contribution.asset + ), + Error::::ProjectSoldOut + ); + }); + } } }