Skip to content

Commit 38af93a

Browse files
authored
πŸ™…πŸ»β€β™‚οΈ Add end block check on participations (#424)
## 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
1 parent 904e5d7 commit 38af93a

File tree

9 files changed

+120
-12
lines changed

9 files changed

+120
-12
lines changed

β€Žintegration-tests/src/tests/otm_edge_cases.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,11 @@ fn otm_fee_below_min_amount_reverts() {
7777

7878
assert!(min_usdt_contribution_otm_fee < usdt_min_balance);
7979

80-
let ct_for_min_usdt_contribution =
81-
PolimecFunding::funding_asset_to_ct_amount_classic(project_id, AcceptedFundingAsset::USDT, min_usdt_contribution);
80+
let ct_for_min_usdt_contribution = PolimecFunding::funding_asset_to_ct_amount_classic(
81+
project_id,
82+
AcceptedFundingAsset::USDT,
83+
min_usdt_contribution,
84+
);
8285

8386
let jwt = get_mock_jwt_with_cid(
8487
bobert.clone(),
@@ -152,8 +155,11 @@ fn after_otm_fee_user_goes_under_ed_reverts() {
152155
let usdt_contribution = usdt_price.reciprocal().unwrap().saturating_mul_int(usd_contribution);
153156
let usdt_otm_fee = usdt_price.reciprocal().unwrap().saturating_mul_int(usd_otm_fee);
154157

155-
let ct_for_contribution =
156-
PolimecFunding::funding_asset_to_ct_amount_classic(project_id, AcceptedFundingAsset::USDT, usdt_contribution);
158+
let ct_for_contribution = PolimecFunding::funding_asset_to_ct_amount_classic(
159+
project_id,
160+
AcceptedFundingAsset::USDT,
161+
usdt_contribution,
162+
);
157163
let jwt = get_mock_jwt_with_cid(
158164
bobert.clone(),
159165
InvestorType::Retail,

β€Žpallets/funding/src/functions/2_evaluation.rs

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ impl<T: Config> Pallet<T> {
9494
ensure!(usd_amount >= T::MinUsdPerEvaluation::get(), Error::<T>::TooLow);
9595
ensure!(project_details.issuer_did != did, Error::<T>::ParticipationToOwnProject);
9696
ensure!(project_details.status == ProjectStatus::EvaluationRound, Error::<T>::IncorrectRound);
97+
ensure!(
98+
project_details.round_duration.started(now) && !project_details.round_duration.ended(now),
99+
Error::<T>::IncorrectRound
100+
);
97101
ensure!(total_evaluations_count < T::MaxEvaluationsPerProject::get(), Error::<T>::TooManyProjectParticipations);
98102
ensure!(user_evaluations_count < T::MaxEvaluationsPerUser::get(), Error::<T>::TooManyUserParticipations);
99103

β€Žpallets/funding/src/functions/3_auction.rs

+4
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ impl<T: Config> Pallet<T> {
8989
ensure!(ct_amount > Zero::zero(), Error::<T>::TooLow);
9090
ensure!(did != project_details.issuer_did, Error::<T>::ParticipationToOwnProject);
9191
ensure!(matches!(project_details.status, ProjectStatus::AuctionRound), Error::<T>::IncorrectRound);
92+
ensure!(
93+
project_details.round_duration.started(now) && !project_details.round_duration.ended(now),
94+
Error::<T>::IncorrectRound
95+
);
9296
ensure!(
9397
project_metadata.participation_currencies.contains(&funding_asset),
9498
Error::<T>::FundingAssetNotAccepted

β€Žpallets/funding/src/functions/4_contribution.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ impl<T: Config> Pallet<T> {
2424

2525
let now = <frame_system::Pallet<T>>::block_number();
2626
let remainder_started = now >= remainder_start;
27-
let round_end = project_details.round_duration.end().ok_or(Error::<T>::ImpossibleState)?;
2827
ensure!(!did_has_winning_bid || remainder_started, Error::<T>::UserHasWinningBid);
29-
ensure!(now < round_end, Error::<T>::TooLateForRound);
28+
ensure!(
29+
project_details.round_duration.started(now) && !project_details.round_duration.ended(now),
30+
Error::<T>::IncorrectRound
31+
);
3032

3133
let buyable_tokens = token_amount.min(project_details.remaining_contribution_tokens);
3234
if buyable_tokens.is_zero() {

β€Žpallets/funding/src/runtime_api.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use crate::traits::BondingRequirementCalculation;
12
#[allow(clippy::wildcard_imports)]
23
use crate::*;
3-
use crate::{traits::BondingRequirementCalculation};
44
use alloc::collections::BTreeMap;
55
use frame_support::traits::fungibles::{Inspect, InspectEnumerable};
66
use itertools::Itertools;
@@ -194,7 +194,7 @@ impl<T: Config> Pallet<T> {
194194
let otm_fee_plmc_percentage = <T as pallet_proxy_bonding::Config>::FeePercentage::get();
195195
let otm_fee_usd_percentage = otm_fee_plmc_percentage / otm_multiplier;
196196

197-
let divisor = FixedU128::from_perbill(otm_fee_usd_percentage) + FixedU128::from_rational(1,1);
197+
let divisor = FixedU128::from_perbill(otm_fee_usd_percentage) + FixedU128::from_rational(1, 1);
198198
let participating_funding_asset_amount =
199199
divisor.reciprocal().unwrap().saturating_mul_int(total_funding_asset_amount);
200200
let fee_funding_asset_amount = total_funding_asset_amount.saturating_sub(participating_funding_asset_amount);

β€Žpallets/funding/src/tests/2_evaluation.rs

+28
Original file line numberDiff line numberDiff line change
@@ -982,5 +982,33 @@ mod evaluate_extrinsic {
982982
);
983983
});
984984
}
985+
986+
#[test]
987+
fn evaluated_after_end_block_before_transitioning_project() {
988+
let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext())));
989+
let issuer = ISSUER_1;
990+
let project_metadata = default_project_metadata(issuer);
991+
let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None);
992+
let project_details = inst.get_project_details(project_id);
993+
let end_block = project_details.round_duration.end().unwrap();
994+
inst.jump_to_block(end_block + 1);
995+
assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::EvaluationRound);
996+
inst.execute(|| {
997+
assert_noop!(
998+
PolimecFunding::evaluate(
999+
RuntimeOrigin::signed(EVALUATOR_1),
1000+
get_mock_jwt_with_cid(
1001+
EVALUATOR_1,
1002+
InvestorType::Retail,
1003+
generate_did_from_account(EVALUATOR_1),
1004+
project_metadata.clone().policy_ipfs_cid.unwrap()
1005+
),
1006+
project_id,
1007+
500 * USD_UNIT,
1008+
),
1009+
Error::<TestRuntime>::IncorrectRound
1010+
);
1011+
});
1012+
}
9851013
}
9861014
}

β€Žpallets/funding/src/tests/3_auction.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,35 @@ mod bid_extrinsic {
18631863
);
18641864
});
18651865
}
1866+
1867+
#[test]
1868+
fn bid_after_end_block_before_transitioning_project() {
1869+
let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext())));
1870+
let project_metadata = default_project_metadata(ISSUER_1);
1871+
let project_id =
1872+
inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, None, default_evaluations());
1873+
let end_block = inst.get_project_details(project_id).round_duration.end.unwrap();
1874+
inst.jump_to_block(end_block + 1);
1875+
assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::AuctionRound);
1876+
inst.execute(|| {
1877+
assert_noop!(
1878+
PolimecFunding::bid(
1879+
RuntimeOrigin::signed(BIDDER_1),
1880+
get_mock_jwt_with_cid(
1881+
BIDDER_1,
1882+
InvestorType::Professional,
1883+
generate_did_from_account(BIDDER_1),
1884+
project_metadata.clone().policy_ipfs_cid.unwrap()
1885+
),
1886+
project_id,
1887+
5000 * CT_UNIT,
1888+
ParticipationMode::Classic(1u8),
1889+
AcceptedFundingAsset::USDT
1890+
),
1891+
Error::<TestRuntime>::IncorrectRound
1892+
);
1893+
});
1894+
}
18661895
}
18671896
}
18681897

β€Žpallets/funding/src/tests/4_contribution.rs

+35
Original file line numberDiff line numberDiff line change
@@ -2184,5 +2184,40 @@ mod contribute_extrinsic {
21842184
);
21852185
});
21862186
}
2187+
2188+
#[test]
2189+
fn contributed_after_end_block_before_transitioning_project() {
2190+
let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext())));
2191+
let project_metadata = default_project_metadata(ISSUER_1);
2192+
let project_id = inst.create_community_contributing_project(
2193+
project_metadata.clone(),
2194+
ISSUER_1,
2195+
None,
2196+
default_evaluations(),
2197+
vec![],
2198+
);
2199+
let end_block = inst.get_project_details(project_id).round_duration.end.unwrap();
2200+
inst.jump_to_block(end_block + 1);
2201+
assert!(matches!(inst.get_project_details(project_id).status, ProjectStatus::CommunityRound(..)));
2202+
2203+
inst.execute(|| {
2204+
assert_noop!(
2205+
PolimecFunding::contribute(
2206+
RuntimeOrigin::signed(BUYER_1),
2207+
get_mock_jwt_with_cid(
2208+
BUYER_1,
2209+
InvestorType::Professional,
2210+
generate_did_from_account(BUYER_1),
2211+
project_metadata.clone().policy_ipfs_cid.unwrap()
2212+
),
2213+
project_id,
2214+
5000 * CT_UNIT,
2215+
ParticipationMode::Classic(1u8),
2216+
AcceptedFundingAsset::USDT
2217+
),
2218+
Error::<TestRuntime>::IncorrectRound
2219+
);
2220+
});
2221+
}
21872222
}
21882223
}

β€Žpallets/funding/src/tests/runtime_api.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ fn funding_asset_to_ct_amount_otm() {
505505
AcceptedFundingAsset::DOT,
506506
dot_participation_amount + dot_fee_amount,
507507
)
508-
.unwrap();
508+
.unwrap();
509509
assert_close_enough!(ct_amount, expected_ct_amount_contribution, Perquintill::from_float(0.9999));
510510
assert_close_enough!(fee_amount, dot_fee_amount, Perquintill::from_float(0.9999));
511511
});
@@ -540,7 +540,7 @@ fn funding_asset_to_ct_amount_otm() {
540540
AcceptedFundingAsset::DOT,
541541
dot_participation_amount + dot_fee_amount,
542542
)
543-
.unwrap();
543+
.unwrap();
544544
assert_close_enough!(ct_amount, expected_ct_amount_contribution, Perquintill::from_float(0.9999f64));
545545
assert_close_enough!(fee_amount, dot_fee_amount, Perquintill::from_float(0.9999f64));
546546
});
@@ -594,7 +594,7 @@ fn funding_asset_to_ct_amount_otm() {
594594
AcceptedFundingAsset::DOT,
595595
dot_participation_amount + dot_fee_amount,
596596
)
597-
.unwrap();
597+
.unwrap();
598598
assert_close_enough!(ct_amount, expected_ct_amount, Perquintill::from_float(0.9999));
599599
assert_close_enough!(fee_amount, dot_fee_amount, Perquintill::from_float(0.9999));
600600
});
@@ -618,7 +618,7 @@ fn funding_asset_to_ct_amount_otm() {
618618
AcceptedFundingAsset::DOT,
619619
dot_participation_amount + dot_fee_amount,
620620
)
621-
.unwrap();
621+
.unwrap();
622622
assert_close_enough!(ct_amount, expected_ct_amount, Perquintill::from_float(0.9999));
623623
assert_close_enough!(fee_amount, dot_fee_amount, Perquintill::from_float(0.9999));
624624
});

0 commit comments

Comments
Β (0)