Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Start failed evaluation settlement #331

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion pallets/funding/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1867,7 +1867,10 @@ mod benchmarks {
}

#[benchmark]
fn end_evaluation_failure() {
fn end_evaluation_failure(
// Insertion attempts in add_to_update_store. Total amount of storage items iterated through in `ProjectsToUpdate`. Leave one free to make the fn succeed
x: Linear<1, { <T as Config>::MaxProjectsToUpdateInsertionAttempts::get() - 1 }>,
) {
// * setup *
let mut inst = BenchInstantiator::<T>::new(None);
<T as Config>::SetPrices::set_prices();
Expand Down Expand Up @@ -1911,6 +1914,8 @@ mod benchmarks {
// move block manually without calling any hooks, to avoid triggering the transition outside the benchmarking context
frame_system::Pallet::<T>::set_block_number(evaluation_end_block + One::one());

fill_projects_to_update::<T>(x, evaluation_end_block + 2u32.into());

// Instead of advancing in time for the automatic `do_evaluation_end` call in on_initialize, we call it directly to benchmark it
#[block]
{
Expand Down
9 changes: 8 additions & 1 deletion pallets/funding/src/functions/2_evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,18 @@ impl<T: Config> Pallet<T> {
// Unsuccessful path
} else {
// * Update storage *

project_details.status = ProjectStatus::FundingFailed;
ProjectsDetails::<T>::insert(project_id, project_details.clone());
let issuer_did = project_details.issuer_did.clone();
DidWithActiveProjects::<T>::set(issuer_did, None);

let insertion_attempts =
match Self::add_to_update_store(now + One::one(), (&project_id, UpdateType::StartSettlement)) {
Ok(insertions) => insertions,
Err(_insertions) => return Err(Error::<T>::TooManyInsertionAttempts.into()),
};

// * Emit events *
Self::deposit_event(
Event::ProjectPhaseTransition {
Expand All @@ -162,7 +169,7 @@ impl<T: Config> Pallet<T> {
.into(),
);
return Ok(PostDispatchInfo {
actual_weight: Some(WeightInfoOf::<T>::end_evaluation_failure()),
actual_weight: Some(WeightInfoOf::<T>::end_evaluation_failure(insertion_attempts)),
pays_fee: Pays::Yes,
});
}
Expand Down
18 changes: 12 additions & 6 deletions pallets/funding/src/functions/6_settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ impl<T: Config> Pallet<T> {

pub fn do_settle_successful_evaluation(evaluation: EvaluationInfoOf<T>, project_id: ProjectId) -> DispatchResult {
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::<T>::WrongSettlementOutcome);

// Based on the results of the funding round, the evaluator is either:
// 1. Slashed
Expand Down Expand Up @@ -136,7 +137,8 @@ impl<T: Config> Pallet<T> {

pub fn do_settle_failed_evaluation(evaluation: EvaluationInfoOf<T>, project_id: ProjectId) -> DispatchResult {
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::WrongSettlementOutcome);

let bond = if matches!(project_details.evaluation_round_info.evaluators_outcome, EvaluatorsOutcome::Slashed) {
Self::slash_evaluator(project_id, &evaluation)?
Expand Down Expand Up @@ -169,7 +171,8 @@ impl<T: Config> Pallet<T> {
let project_metadata = ProjectsMetadata::<T>::get(project_id).ok_or(Error::<T>::ProjectMetadataNotFound)?;
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;

ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::<T>::WrongSettlementOutcome);
ensure!(
matches!(bid.status, BidStatus::Accepted | BidStatus::PartiallyAccepted(..)),
Error::<T>::ImpossibleState
Expand Down Expand Up @@ -231,7 +234,8 @@ impl<T: Config> Pallet<T> {

pub fn do_settle_failed_bid(bid: BidInfoOf<T>, project_id: ProjectId) -> DispatchResult {
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::WrongSettlementOutcome);

let bidder = bid.bidder;

Expand All @@ -258,7 +262,8 @@ impl<T: Config> Pallet<T> {
// Ensure that:
// 1. The project is in the FundingSuccessful state
// 2. The contribution token exists
ensure!(project_details.status == ProjectStatus::FundingSuccessful, Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingSuccessful), Error::<T>::WrongSettlementOutcome);
ensure!(T::ContributionTokenCurrency::asset_exists(project_id), Error::<T>::TooEarlyForRound);

let contributor = contribution.contributor;
Expand Down Expand Up @@ -316,7 +321,8 @@ impl<T: Config> Pallet<T> {

pub fn do_settle_failed_contribution(contribution: ContributionInfoOf<T>, project_id: ProjectId) -> DispatchResult {
let project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::IncorrectRound);
ensure!(matches!(project_details.funding_end_block, Some(_)), Error::<T>::SettlementNotStarted);
ensure!(matches!(project_details.status, ProjectStatus::FundingFailed), Error::<T>::WrongSettlementOutcome);

// Check if the bidder has a future deposit held
let contributor = contribution.contributor;
Expand Down
4 changes: 4 additions & 0 deletions pallets/funding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,10 @@ pub mod pallet {
WrongParaId,
/// Migration channel is not ready for migrations.
ChannelNotReady,
/// Settlement for this project has not yet started.
SettlementNotStarted,
/// Wanted to settle as successful when it failed, or vice versa.
WrongSettlementOutcome,
/// User still has participations that need to be settled before migration.
ParticipationsNotSettled,
}
Expand Down
11 changes: 10 additions & 1 deletion pallets/funding/src/tests/2_evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,12 @@ mod round_flow {

assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed);

// Check that on_idle has unlocked the failed bonds
// Cannot settle before the settlement starts
inst.settle_project(project_id).expect_err("Settlement should not be possible yet");

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.settle_project(project_id).unwrap();
inst.do_free_plmc_assertions(expected_evaluator_balances);
}
Expand Down Expand Up @@ -678,6 +683,10 @@ mod evaluate_extrinsic {

let treasury_account = <TestRuntime as Config>::ProtocolGrowthTreasury::get();
let pre_slash_treasury_balance = inst.get_free_plmc_balance_for(treasury_account);

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.execute(|| {
PolimecFunding::settle_failed_evaluation(
RuntimeOrigin::signed(EVALUATOR_4),
Expand Down
3 changes: 3 additions & 0 deletions pallets/funding/src/tests/3_auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,9 @@ mod bid_extrinsic {
assert_eq!(bid_held_balance, frozen_amount);
assert_eq!(frozen_balance, frozen_amount);

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.execute(|| {
PolimecFunding::settle_failed_bid(RuntimeOrigin::signed(BIDDER_4), project_id, BIDDER_4, 0).unwrap();
});
Expand Down
3 changes: 3 additions & 0 deletions pallets/funding/src/tests/4_community.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,9 @@ mod community_contribute_extrinsic {
assert_eq!(bid_held_balance, frozen_amount);
assert_eq!(frozen_balance, frozen_amount);

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.execute(|| {
PolimecFunding::settle_failed_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0)
.unwrap();
Expand Down
3 changes: 3 additions & 0 deletions pallets/funding/src/tests/5_remainder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,9 @@ mod remaining_contribute_extrinsic {
};
assert_eq!(account_data, expected_account_data);

let settlement_block = inst.get_update_block(project_id, &UpdateType::StartSettlement).unwrap();
inst.jump_to_block(settlement_block);

inst.execute(|| {
PolimecFunding::settle_failed_contribution(RuntimeOrigin::signed(BUYER_4), project_id, BUYER_4, 0)
.unwrap();
Expand Down
12 changes: 7 additions & 5 deletions pallets/funding/src/tests/6_funding_end.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod round_flow {

#[test]
fn evaluator_slash_is_decided() {
let (mut inst, project_id) = create_project_with_funding_percentage(20, None);
let (mut inst, project_id) = create_project_with_funding_percentage(20, None, true);
assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed);
assert_eq!(
inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome,
Expand All @@ -19,7 +19,7 @@ mod round_flow {
#[test]
fn evaluator_unchanged_is_decided() {
let (mut inst, project_id) =
create_project_with_funding_percentage(80, Some(FundingOutcomeDecision::AcceptFunding));
create_project_with_funding_percentage(80, Some(FundingOutcomeDecision::AcceptFunding), true);
assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingSuccessful);
assert_eq!(
inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome,
Expand All @@ -29,7 +29,7 @@ mod round_flow {

#[test]
fn evaluator_reward_is_decided() {
let (mut inst, project_id) = create_project_with_funding_percentage(95, None);
let (mut inst, project_id) = create_project_with_funding_percentage(95, None, true);
let project_details = inst.get_project_details(project_id);
let project_metadata = inst.get_project_metadata(project_id);
assert_eq!(project_details.status, ProjectStatus::FundingSuccessful);
Expand Down Expand Up @@ -85,6 +85,7 @@ mod decide_project_outcome {
let _ = create_project_with_funding_percentage(
funding_percent,
Some(FundingOutcomeDecision::AcceptFunding),
true,
);
}
}
Expand All @@ -95,14 +96,15 @@ mod decide_project_outcome {
let _ = create_project_with_funding_percentage(
funding_percent,
Some(FundingOutcomeDecision::RejectFunding),
true,
);
}
}

#[test]
fn automatic_fail_less_eq_33_percent() {
for funding_percent in (1..=33).step_by(5) {
let _ = create_project_with_funding_percentage(funding_percent, None);
let _ = create_project_with_funding_percentage(funding_percent, None, true);
}
}

Expand Down Expand Up @@ -144,7 +146,7 @@ mod decide_project_outcome {
#[test]
fn automatic_success_bigger_eq_90_percent() {
for funding_percent in (90..=100).step_by(2) {
let _ = create_project_with_funding_percentage(funding_percent, None);
let _ = create_project_with_funding_percentage(funding_percent, None, true);
}
}
}
Expand Down
Loading