From 38af93ad077649afaddab3a03a78043dd7c25cc6 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Tue, 22 Oct 2024 09:48:38 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=99=85=F0=9F=8F=BB=E2=80=8D=E2=99=82?= =?UTF-8?q?=EF=B8=8F=20Add=20end=20block=20check=20on=20participations=20(?= =?UTF-8?q?#424)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What? Disallow participations done after the round end, and before the project was transitioned to a new state ## Why? This was unintended behavior ## How? One more ensure in each function. Quite simple ## Testing? One new failure test for evaluate/bid/contribute --- integration-tests/src/tests/otm_edge_cases.rs | 14 +++++--- pallets/funding/src/functions/2_evaluation.rs | 4 +++ pallets/funding/src/functions/3_auction.rs | 4 +++ .../funding/src/functions/4_contribution.rs | 6 ++-- pallets/funding/src/runtime_api.rs | 4 +-- pallets/funding/src/tests/2_evaluation.rs | 28 +++++++++++++++ pallets/funding/src/tests/3_auction.rs | 29 +++++++++++++++ pallets/funding/src/tests/4_contribution.rs | 35 +++++++++++++++++++ pallets/funding/src/tests/runtime_api.rs | 8 ++--- 9 files changed, 120 insertions(+), 12 deletions(-) diff --git a/integration-tests/src/tests/otm_edge_cases.rs b/integration-tests/src/tests/otm_edge_cases.rs index b34c83f96..9b70d63a1 100644 --- a/integration-tests/src/tests/otm_edge_cases.rs +++ b/integration-tests/src/tests/otm_edge_cases.rs @@ -77,8 +77,11 @@ fn otm_fee_below_min_amount_reverts() { assert!(min_usdt_contribution_otm_fee < usdt_min_balance); - let ct_for_min_usdt_contribution = - PolimecFunding::funding_asset_to_ct_amount_classic(project_id, AcceptedFundingAsset::USDT, min_usdt_contribution); + let ct_for_min_usdt_contribution = PolimecFunding::funding_asset_to_ct_amount_classic( + project_id, + AcceptedFundingAsset::USDT, + min_usdt_contribution, + ); let jwt = get_mock_jwt_with_cid( bobert.clone(), @@ -152,8 +155,11 @@ fn after_otm_fee_user_goes_under_ed_reverts() { let usdt_contribution = usdt_price.reciprocal().unwrap().saturating_mul_int(usd_contribution); let usdt_otm_fee = usdt_price.reciprocal().unwrap().saturating_mul_int(usd_otm_fee); - let ct_for_contribution = - PolimecFunding::funding_asset_to_ct_amount_classic(project_id, AcceptedFundingAsset::USDT, usdt_contribution); + let ct_for_contribution = PolimecFunding::funding_asset_to_ct_amount_classic( + project_id, + AcceptedFundingAsset::USDT, + usdt_contribution, + ); let jwt = get_mock_jwt_with_cid( bobert.clone(), InvestorType::Retail, diff --git a/pallets/funding/src/functions/2_evaluation.rs b/pallets/funding/src/functions/2_evaluation.rs index b97e071a2..d577ada0c 100644 --- a/pallets/funding/src/functions/2_evaluation.rs +++ b/pallets/funding/src/functions/2_evaluation.rs @@ -94,6 +94,10 @@ impl Pallet { ensure!(usd_amount >= T::MinUsdPerEvaluation::get(), Error::::TooLow); ensure!(project_details.issuer_did != did, Error::::ParticipationToOwnProject); ensure!(project_details.status == ProjectStatus::EvaluationRound, Error::::IncorrectRound); + ensure!( + project_details.round_duration.started(now) && !project_details.round_duration.ended(now), + Error::::IncorrectRound + ); ensure!(total_evaluations_count < T::MaxEvaluationsPerProject::get(), Error::::TooManyProjectParticipations); ensure!(user_evaluations_count < T::MaxEvaluationsPerUser::get(), Error::::TooManyUserParticipations); diff --git a/pallets/funding/src/functions/3_auction.rs b/pallets/funding/src/functions/3_auction.rs index 2c6c94852..dae7bea08 100644 --- a/pallets/funding/src/functions/3_auction.rs +++ b/pallets/funding/src/functions/3_auction.rs @@ -89,6 +89,10 @@ impl Pallet { ensure!(ct_amount > Zero::zero(), Error::::TooLow); ensure!(did != project_details.issuer_did, Error::::ParticipationToOwnProject); ensure!(matches!(project_details.status, ProjectStatus::AuctionRound), Error::::IncorrectRound); + ensure!( + project_details.round_duration.started(now) && !project_details.round_duration.ended(now), + Error::::IncorrectRound + ); ensure!( project_metadata.participation_currencies.contains(&funding_asset), Error::::FundingAssetNotAccepted diff --git a/pallets/funding/src/functions/4_contribution.rs b/pallets/funding/src/functions/4_contribution.rs index 5f334d45d..5826fcf91 100644 --- a/pallets/funding/src/functions/4_contribution.rs +++ b/pallets/funding/src/functions/4_contribution.rs @@ -24,9 +24,11 @@ impl Pallet { let now = >::block_number(); let remainder_started = now >= remainder_start; - let round_end = project_details.round_duration.end().ok_or(Error::::ImpossibleState)?; ensure!(!did_has_winning_bid || remainder_started, Error::::UserHasWinningBid); - ensure!(now < round_end, Error::::TooLateForRound); + ensure!( + project_details.round_duration.started(now) && !project_details.round_duration.ended(now), + Error::::IncorrectRound + ); let buyable_tokens = token_amount.min(project_details.remaining_contribution_tokens); if buyable_tokens.is_zero() { diff --git a/pallets/funding/src/runtime_api.rs b/pallets/funding/src/runtime_api.rs index 8f41e5ea8..bfcc9752d 100644 --- a/pallets/funding/src/runtime_api.rs +++ b/pallets/funding/src/runtime_api.rs @@ -1,6 +1,6 @@ +use crate::traits::BondingRequirementCalculation; #[allow(clippy::wildcard_imports)] use crate::*; -use crate::{traits::BondingRequirementCalculation}; use alloc::collections::BTreeMap; use frame_support::traits::fungibles::{Inspect, InspectEnumerable}; use itertools::Itertools; @@ -194,7 +194,7 @@ impl Pallet { let otm_fee_plmc_percentage = ::FeePercentage::get(); let otm_fee_usd_percentage = otm_fee_plmc_percentage / otm_multiplier; - let divisor = FixedU128::from_perbill(otm_fee_usd_percentage) + FixedU128::from_rational(1,1); + let divisor = FixedU128::from_perbill(otm_fee_usd_percentage) + FixedU128::from_rational(1, 1); let participating_funding_asset_amount = divisor.reciprocal().unwrap().saturating_mul_int(total_funding_asset_amount); let fee_funding_asset_amount = total_funding_asset_amount.saturating_sub(participating_funding_asset_amount); diff --git a/pallets/funding/src/tests/2_evaluation.rs b/pallets/funding/src/tests/2_evaluation.rs index c5e0d6800..8895e4650 100644 --- a/pallets/funding/src/tests/2_evaluation.rs +++ b/pallets/funding/src/tests/2_evaluation.rs @@ -982,5 +982,33 @@ mod evaluate_extrinsic { ); }); } + + #[test] + fn evaluated_after_end_block_before_transitioning_project() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let issuer = ISSUER_1; + let project_metadata = default_project_metadata(issuer); + let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); + let project_details = inst.get_project_details(project_id); + let end_block = project_details.round_duration.end().unwrap(); + inst.jump_to_block(end_block + 1); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationRound); + inst.execute(|| { + assert_noop!( + PolimecFunding::evaluate( + RuntimeOrigin::signed(EVALUATOR_1), + get_mock_jwt_with_cid( + EVALUATOR_1, + InvestorType::Retail, + generate_did_from_account(EVALUATOR_1), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), + project_id, + 500 * USD_UNIT, + ), + Error::::IncorrectRound + ); + }); + } } } diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index 154fa3d9d..62e4707b4 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -1863,6 +1863,35 @@ mod bid_extrinsic { ); }); } + + #[test] + fn bid_after_end_block_before_transitioning_project() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = + inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations()); + let end_block = inst.get_project_details(project_id).round_duration.end.unwrap(); + inst.jump_to_block(end_block + 1); + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionRound); + inst.execute(|| { + assert_noop!( + PolimecFunding::bid( + RuntimeOrigin::signed(BIDDER_1), + get_mock_jwt_with_cid( + BIDDER_1, + InvestorType::Professional, + generate_did_from_account(BIDDER_1), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), + project_id, + 5000 * CT_UNIT, + ParticipationMode::Classic(1u8), + AcceptedFundingAsset::USDT + ), + Error::::IncorrectRound + ); + }); + } } } diff --git a/pallets/funding/src/tests/4_contribution.rs b/pallets/funding/src/tests/4_contribution.rs index 587c240d6..dafb7aec3 100644 --- a/pallets/funding/src/tests/4_contribution.rs +++ b/pallets/funding/src/tests/4_contribution.rs @@ -2184,5 +2184,40 @@ mod contribute_extrinsic { ); }); } + + #[test] + fn contributed_after_end_block_before_transitioning_project() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_community_contributing_project( + project_metadata.clone(), + ISSUER_1, + None, + default_evaluations(), + vec![], + ); + let end_block = inst.get_project_details(project_id).round_duration.end.unwrap(); + inst.jump_to_block(end_block + 1); + assert!(matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(..))); + + inst.execute(|| { + assert_noop!( + PolimecFunding::contribute( + RuntimeOrigin::signed(BUYER_1), + get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Professional, + generate_did_from_account(BUYER_1), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), + project_id, + 5000 * CT_UNIT, + ParticipationMode::Classic(1u8), + AcceptedFundingAsset::USDT + ), + Error::::IncorrectRound + ); + }); + } } } diff --git a/pallets/funding/src/tests/runtime_api.rs b/pallets/funding/src/tests/runtime_api.rs index 00c9abbbc..135683f8b 100644 --- a/pallets/funding/src/tests/runtime_api.rs +++ b/pallets/funding/src/tests/runtime_api.rs @@ -505,7 +505,7 @@ fn funding_asset_to_ct_amount_otm() { AcceptedFundingAsset::DOT, dot_participation_amount + dot_fee_amount, ) - .unwrap(); + .unwrap(); assert_close_enough!(ct_amount, expected_ct_amount_contribution, Perquintill::from_float(0.9999)); assert_close_enough!(fee_amount, dot_fee_amount, Perquintill::from_float(0.9999)); }); @@ -540,7 +540,7 @@ fn funding_asset_to_ct_amount_otm() { AcceptedFundingAsset::DOT, dot_participation_amount + dot_fee_amount, ) - .unwrap(); + .unwrap(); assert_close_enough!(ct_amount, expected_ct_amount_contribution, Perquintill::from_float(0.9999f64)); assert_close_enough!(fee_amount, dot_fee_amount, Perquintill::from_float(0.9999f64)); }); @@ -594,7 +594,7 @@ fn funding_asset_to_ct_amount_otm() { AcceptedFundingAsset::DOT, dot_participation_amount + dot_fee_amount, ) - .unwrap(); + .unwrap(); assert_close_enough!(ct_amount, expected_ct_amount, Perquintill::from_float(0.9999)); assert_close_enough!(fee_amount, dot_fee_amount, Perquintill::from_float(0.9999)); }); @@ -618,7 +618,7 @@ fn funding_asset_to_ct_amount_otm() { AcceptedFundingAsset::DOT, dot_participation_amount + dot_fee_amount, ) - .unwrap(); + .unwrap(); assert_close_enough!(ct_amount, expected_ct_amount, Perquintill::from_float(0.9999)); assert_close_enough!(fee_amount, dot_fee_amount, Perquintill::from_float(0.9999)); });