diff --git a/pallets/funding/src/tests/2_evaluation.rs b/pallets/funding/src/tests/2_evaluation.rs index ef6d861d6..235e28ce7 100644 --- a/pallets/funding/src/tests/2_evaluation.rs +++ b/pallets/funding/src/tests/2_evaluation.rs @@ -484,6 +484,12 @@ mod evaluate_extrinsic { #[cfg(test)] mod success { use super::*; + use frame_support::traits::{ + fungible::{InspectFreeze, Mutate}, + tokens::Preservation, + }; + use pallet_balances::AccountData; + use sp_runtime::DispatchError::Token; #[test] fn all_investor_types() { @@ -618,6 +624,116 @@ mod evaluate_extrinsic { assert_eq!(stored_evaluation, &expected_evaluation_item); }); } + + #[test] + fn can_evaluate_with_frozen_tokens() { + 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); + + let evaluation = UserToUSDBalance::new(EVALUATOR_4, 500 * USD_UNIT); + let plmc_required = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()]); + let frozen_amount = plmc_required[0].plmc_amount; + let plmc_existential_deposits = plmc_required.accounts().existential_deposits(); + + inst.mint_plmc_to(plmc_existential_deposits); + inst.mint_plmc_to(plmc_required.clone()); + + inst.execute(|| { + mock::Balances::set_freeze(&(), &EVALUATOR_4, plmc_required[0].plmc_amount).unwrap(); + }); + + inst.execute(|| { + assert_noop!( + Balances::transfer_allow_death(RuntimeOrigin::signed(EVALUATOR_4), ISSUER_1, frozen_amount,), + TokenError::Frozen + ); + }); + + inst.execute(|| { + assert_ok!(PolimecFunding::evaluate( + RuntimeOrigin::signed(EVALUATOR_4), + get_mock_jwt_with_cid( + EVALUATOR_4, + InvestorType::Retail, + generate_did_from_account(EVALUATOR_4), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), + project_id, + evaluation.usd_amount + )); + }); + + let new_evaluations = default_evaluations(); + let new_plmc_required = inst.calculate_evaluation_plmc_spent(new_evaluations.clone()); + let new_plmc_existential_deposits = new_plmc_required.accounts().existential_deposits(); + inst.mint_plmc_to(new_plmc_existential_deposits); + inst.mint_plmc_to(new_plmc_required.clone()); + inst.evaluate_for_users(project_id, new_evaluations).unwrap(); + + inst.start_auction(project_id, ISSUER_1).unwrap(); + inst.start_community_funding(project_id).unwrap(); + inst.start_remainder_or_end_funding(project_id).unwrap(); + inst.finish_funding(project_id).unwrap(); + + assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::FundingFailed); + + let free_balance = inst.get_free_plmc_balance_for(EVALUATOR_4); + let evaluation_held_balance = + inst.get_reserved_plmc_balance_for(EVALUATOR_4, HoldReason::Evaluation(project_id).into()); + let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &EVALUATOR_4)); + let account_data = inst.execute(|| System::account(&EVALUATOR_4)).data; + + assert_eq!(free_balance, inst.get_ed()); + assert_eq!(evaluation_held_balance, frozen_amount); + assert_eq!(frozen_balance, frozen_amount); + let expected_account_data = AccountData { + free: inst.get_ed(), + reserved: frozen_amount, + frozen: frozen_amount, + flags: Default::default(), + }; + assert_eq!(account_data, expected_account_data); + + let treasury_account = ::ProtocolGrowthTreasury::get(); + let pre_slash_treasury_balance = inst.get_free_plmc_balance_for(treasury_account); + inst.execute(|| { + PolimecFunding::settle_failed_evaluation( + RuntimeOrigin::signed(EVALUATOR_4), + project_id, + EVALUATOR_4, + 0, + ) + .unwrap(); + }); + + let post_slash_treasury_balance = inst.get_free_plmc_balance_for(treasury_account); + let free_balance = inst.get_free_plmc_balance_for(EVALUATOR_4); + let evaluation_held_balance = + inst.get_reserved_plmc_balance_for(EVALUATOR_4, HoldReason::Evaluation(project_id).into()); + let frozen_balance = inst.execute(|| mock::Balances::balance_frozen(&(), &EVALUATOR_4)); + let account_data = inst.execute(|| System::account(&EVALUATOR_4)).data; + + let post_slash_evaluation_plmc = + frozen_amount - (::EvaluatorSlash::get() * frozen_amount); + assert_eq!(free_balance, inst.get_ed() + post_slash_evaluation_plmc); + assert_eq!(evaluation_held_balance, Zero::zero()); + assert_eq!(frozen_balance, frozen_amount); + let expected_account_data = AccountData { + free: inst.get_ed() + post_slash_evaluation_plmc, + reserved: Zero::zero(), + frozen: frozen_amount, + flags: Default::default(), + }; + assert_eq!(account_data, expected_account_data); + + assert!(account_data.frozen > account_data.free); + assert_eq!( + post_slash_treasury_balance, + pre_slash_treasury_balance + ::EvaluatorSlash::get() * frozen_amount + ); + } } #[cfg(test)] diff --git a/pallets/funding/src/tests/7_settlement.rs b/pallets/funding/src/tests/7_settlement.rs index aeca7c25c..c6e6eb140 100644 --- a/pallets/funding/src/tests/7_settlement.rs +++ b/pallets/funding/src/tests/7_settlement.rs @@ -318,6 +318,64 @@ mod settle_failed_evaluation_extrinsic { #[cfg(test)] mod success { use super::*; + + #[test] + fn evaluation_unchanged() { + let percentage = 89u64; + + let (mut inst, project_id) = + create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding)); + + let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); + let evaluator = first_evaluation.evaluator; + let prev_balance = inst.get_free_plmc_balance_for(evaluator); + + assert_eq!( + inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, + EvaluatorsOutcomeOf::::Unchanged + ); + + assert_ok!(inst.execute(|| PolimecFunding::settle_failed_evaluation( + RuntimeOrigin::signed(evaluator), + project_id, + evaluator, + first_evaluation.id + ))); + + let post_balance = inst.get_free_plmc_balance_for(evaluator); + assert_eq!(post_balance, prev_balance + first_evaluation.current_plmc_bond); + } + + #[test] + fn evaluation_slashed() { + let percentage = 50u64; + let (mut inst, project_id) = + create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding)); + + let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); + let evaluator = first_evaluation.evaluator; + let prev_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; + + assert_eq!( + inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, + EvaluatorsOutcomeOf::::Slashed + ); + + assert_ok!(inst.execute(|| PolimecFunding::settle_failed_evaluation( + RuntimeOrigin::signed(evaluator), + project_id, + evaluator, + first_evaluation.id + ))); + + let post_balance = inst.get_free_plmc_balances_for(vec![evaluator])[0].plmc_amount; + assert_eq!( + post_balance, + prev_balance + + (Percent::from_percent(100) - ::EvaluatorSlash::get()) * + first_evaluation.current_plmc_bond + ); + } } #[cfg(test)] @@ -434,91 +492,6 @@ mod settle_failed_contribution_extrinsic { } } -/// Test that the correct amount of PLMC is slashed from the evaluator independent of the -/// project outcome. -#[test] -fn evaluator_slashed_if_between_33_and_75() { - let percentage = 50u64; - let project_1 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding)); - let project_2 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding)); - let projects = vec![project_1, project_2]; - - for (mut inst, project_id) in projects { - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - - inst.execute(|| { - let prev_balance = ::NativeCurrency::balance(&evaluator); - match ProjectsDetails::::get(project_id).unwrap().status { - ProjectStatus::FundingSuccessful => { - assert_ok!(crate::Pallet::::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - )); - }, - ProjectStatus::FundingFailed => { - assert_ok!(crate::Pallet::::settle_failed_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - )); - }, - _ => panic!("unexpected project status"), - } - let balance = ::NativeCurrency::balance(&evaluator); - assert_eq!( - balance, - prev_balance + - (Percent::from_percent(100) - ::EvaluatorSlash::get()) * - first_evaluation.current_plmc_bond - ); - }); - } -} - -// Test that the evaluators PLMC bond is not slashed if the project is between 76 and 89 -// percent funded independent of the project outcome. -#[test] -fn evaluator_plmc_unchanged_between_76_and_89() { - let percentage = 80u64; - let project_1 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::AcceptFunding)); - let project_2 = create_project_with_funding_percentage(percentage, Some(FundingOutcomeDecision::RejectFunding)); - let projects = vec![project_1, project_2]; - - for (mut inst, project_id) in projects { - let first_evaluation = inst.get_evaluations(project_id).into_iter().next().unwrap(); - let evaluator = first_evaluation.evaluator; - - inst.execute(|| { - let prev_balance = ::NativeCurrency::balance(&evaluator); - match ProjectsDetails::::get(project_id).unwrap().status { - ProjectStatus::FundingSuccessful => { - assert_ok!(crate::Pallet::::settle_successful_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - )); - }, - ProjectStatus::FundingFailed => { - assert_ok!(crate::Pallet::::settle_failed_evaluation( - RuntimeOrigin::signed(evaluator), - project_id, - evaluator, - first_evaluation.id - )); - }, - _ => panic!("unexpected project status"), - } - let balance = ::NativeCurrency::balance(&evaluator); - assert_eq!(balance, prev_balance + first_evaluation.current_plmc_bond); - }); - } -} - #[test] fn bid_is_correctly_settled_for_successful_project() { let percentage = 100u64;