Skip to content

Commit

Permalink
πŸ™…πŸ»β€β™‚οΈ Add end block check on participations (#424)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
JuaniRios authored Oct 22, 2024
1 parent 904e5d7 commit 38af93a
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 12 deletions.
14 changes: 10 additions & 4 deletions integration-tests/src/tests/otm_edge_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions pallets/funding/src/functions/2_evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ impl<T: Config> Pallet<T> {
ensure!(usd_amount >= T::MinUsdPerEvaluation::get(), Error::<T>::TooLow);
ensure!(project_details.issuer_did != did, Error::<T>::ParticipationToOwnProject);
ensure!(project_details.status == ProjectStatus::EvaluationRound, Error::<T>::IncorrectRound);
ensure!(
project_details.round_duration.started(now) && !project_details.round_duration.ended(now),
Error::<T>::IncorrectRound
);
ensure!(total_evaluations_count < T::MaxEvaluationsPerProject::get(), Error::<T>::TooManyProjectParticipations);
ensure!(user_evaluations_count < T::MaxEvaluationsPerUser::get(), Error::<T>::TooManyUserParticipations);

Expand Down
4 changes: 4 additions & 0 deletions pallets/funding/src/functions/3_auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ impl<T: Config> Pallet<T> {
ensure!(ct_amount > Zero::zero(), Error::<T>::TooLow);
ensure!(did != project_details.issuer_did, Error::<T>::ParticipationToOwnProject);
ensure!(matches!(project_details.status, ProjectStatus::AuctionRound), Error::<T>::IncorrectRound);
ensure!(
project_details.round_duration.started(now) && !project_details.round_duration.ended(now),
Error::<T>::IncorrectRound
);
ensure!(
project_metadata.participation_currencies.contains(&funding_asset),
Error::<T>::FundingAssetNotAccepted
Expand Down
6 changes: 4 additions & 2 deletions pallets/funding/src/functions/4_contribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ impl<T: Config> Pallet<T> {

let now = <frame_system::Pallet<T>>::block_number();
let remainder_started = now >= remainder_start;
let round_end = project_details.round_duration.end().ok_or(Error::<T>::ImpossibleState)?;
ensure!(!did_has_winning_bid || remainder_started, Error::<T>::UserHasWinningBid);
ensure!(now < round_end, Error::<T>::TooLateForRound);
ensure!(
project_details.round_duration.started(now) && !project_details.round_duration.ended(now),
Error::<T>::IncorrectRound
);

let buyable_tokens = token_amount.min(project_details.remaining_contribution_tokens);
if buyable_tokens.is_zero() {
Expand Down
4 changes: 2 additions & 2 deletions pallets/funding/src/runtime_api.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -194,7 +194,7 @@ impl<T: Config> Pallet<T> {
let otm_fee_plmc_percentage = <T as pallet_proxy_bonding::Config>::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);
Expand Down
28 changes: 28 additions & 0 deletions pallets/funding/src/tests/2_evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<TestRuntime>::IncorrectRound
);
});
}
}
}
29 changes: 29 additions & 0 deletions pallets/funding/src/tests/3_auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<TestRuntime>::IncorrectRound
);
});
}
}
}

Expand Down
35 changes: 35 additions & 0 deletions pallets/funding/src/tests/4_contribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<TestRuntime>::IncorrectRound
);
});
}
}
}
8 changes: 4 additions & 4 deletions pallets/funding/src/tests/runtime_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
});
Expand Down Expand Up @@ -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));
});
Expand Down Expand Up @@ -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));
});
Expand All @@ -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));
});
Expand Down

0 comments on commit 38af93a

Please sign in to comment.