From 8f206da2d7056ec41bdec9b3bb853eb50cfd5686 Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Thu, 8 May 2025 17:45:24 +0200 Subject: [PATCH] draft: use PLMC amount in evaluation --- pallets/funding/src/functions/2_evaluation.rs | 43 ++++------ .../funding/src/instantiator/calculations.rs | 82 ++++++++++--------- .../src/instantiator/chain_interactions.rs | 4 +- pallets/funding/src/instantiator/mod.rs | 2 +- pallets/funding/src/instantiator/types.rs | 20 ++--- pallets/funding/src/lib.rs | 9 +- pallets/funding/src/mock.rs | 2 - pallets/funding/src/tests/2_evaluation.rs | 73 +++++++++-------- pallets/funding/src/tests/3_auction.rs | 6 +- pallets/funding/src/tests/4_funding_end.rs | 15 ++-- pallets/funding/src/tests/5_settlement.rs | 29 ++++--- pallets/funding/src/tests/misc.rs | 68 --------------- pallets/funding/src/tests/mod.rs | 7 +- pallets/funding/src/tests/runtime_api.rs | 25 +++--- pallets/on-slash-vesting/src/test.rs | 4 +- polimec-common/common/src/lib.rs | 1 + 16 files changed, 161 insertions(+), 229 deletions(-) diff --git a/pallets/funding/src/functions/2_evaluation.rs b/pallets/funding/src/functions/2_evaluation.rs index 96463de03..b918952c1 100644 --- a/pallets/funding/src/functions/2_evaluation.rs +++ b/pallets/funding/src/functions/2_evaluation.rs @@ -76,23 +76,22 @@ impl Pallet { pub fn do_evaluate( evaluator: &AccountIdOf, project_id: ProjectId, - usd_amount: Balance, + plmc_bond: Balance, did: Did, whitelisted_policy: Cid, receiving_account: Junction, ) -> DispatchResult { // * Get variables * let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectMetadataNotFound)?; - let mut project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; - let now = ::BlockNumberProvider::current_block_number(); + let project_details = ProjectsDetails::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let now = BlockProviderFor::::current_block_number(); let evaluation_id = NextEvaluationId::::get(); - let plmc_usd_price = >::get_decimals_aware_price(&Location::here(), PLMC_DECIMALS) + let plmc_usd_price = PriceProviderOf::::get_decimals_aware_price(&Location::here(), PLMC_DECIMALS) .ok_or(Error::::PriceNotFound)?; let early_evaluation_reward_threshold_usd = T::EvaluationSuccessThreshold::get() * project_details.fundraising_target_usd; - let evaluation_round_info = &mut project_details.evaluation_round_info; let project_policy = project_metadata.policy_ipfs_cid.ok_or(Error::::ImpossibleState)?; - let project_metadata = ProjectsMetadata::::get(project_id).ok_or(Error::::ProjectDetailsNotFound)?; + let usd_amount = plmc_usd_price.checked_mul_int(plmc_bond).ok_or(Error::::BadMath)?; // * Validity Checks * ensure!(project_policy == whitelisted_policy, Error::::PolicyMismatch); @@ -108,24 +107,11 @@ impl Pallet { Error::::UnsupportedReceiverAccountJunction ); - let plmc_bond = plmc_usd_price - .reciprocal() - .ok_or(Error::::BadMath)? - .checked_mul_int(usd_amount) - .ok_or(Error::::BadMath)?; - let previous_total_evaluation_bonded_usd = evaluation_round_info.total_bonded_usd; - - let remaining_bond_to_reach_threshold = - early_evaluation_reward_threshold_usd.saturating_sub(previous_total_evaluation_bonded_usd); - - let early_usd_amount = if usd_amount <= remaining_bond_to_reach_threshold { - usd_amount - } else { - remaining_bond_to_reach_threshold - }; - + let previously_bonded_usd = project_details.evaluation_round_info.total_bonded_usd; + let remaining_for_early_reward_usd = + early_evaluation_reward_threshold_usd.saturating_sub(previously_bonded_usd); + let early_usd_amount = usd_amount.min(remaining_for_early_reward_usd); let late_usd_amount = usd_amount.checked_sub(early_usd_amount).ok_or(Error::::BadMath)?; - let new_evaluation = EvaluationInfoOf:: { id: evaluation_id, did: did.clone(), @@ -142,9 +128,14 @@ impl Pallet { T::NativeCurrency::hold(&HoldReason::Evaluation.into(), evaluator, plmc_bond)?; Evaluations::::insert((project_id, evaluator, evaluation_id), new_evaluation); NextEvaluationId::::set(evaluation_id.saturating_add(One::one())); - evaluation_round_info.total_bonded_usd = evaluation_round_info.total_bonded_usd.saturating_add(usd_amount); - evaluation_round_info.total_bonded_plmc = evaluation_round_info.total_bonded_plmc.saturating_add(plmc_bond); - ProjectsDetails::::insert(project_id, project_details); + ProjectsDetails::::mutate(project_id, |details| { + if let Some(details) = details { + details.evaluation_round_info.total_bonded_usd = + details.evaluation_round_info.total_bonded_usd.saturating_add(usd_amount); + details.evaluation_round_info.total_bonded_plmc = + details.evaluation_round_info.total_bonded_plmc.saturating_add(plmc_bond); + } + }); // * Emit events * Self::deposit_event(Event::Evaluation { diff --git a/pallets/funding/src/instantiator/calculations.rs b/pallets/funding/src/instantiator/calculations.rs index e14904b4e..41cf9d8ae 100644 --- a/pallets/funding/src/instantiator/calculations.rs +++ b/pallets/funding/src/instantiator/calculations.rs @@ -30,17 +30,7 @@ impl< &mut self, evaluations: Vec>, ) -> Vec> { - let plmc_usd_price = - self.execute(|| >::get_decimals_aware_price(&Location::here(), PLMC_DECIMALS).unwrap()); - - let mut output = Vec::new(); - for eval in evaluations { - let usd_bond = eval.usd_amount; - let plmc_bond = plmc_usd_price.reciprocal().unwrap().saturating_mul_int(usd_bond); - - output.push(UserToPLMCBalance::new(eval.account, plmc_bond)); - } - output + evaluations.into_iter().map(|eval| UserToPLMCBalance::new(eval.account, eval.plmc_amount)).collect() } // A single bid can be split into multiple buckets. This function splits the bid into multiple ones at different prices. @@ -353,32 +343,30 @@ impl< output } - pub fn generate_evaluations_from_total_usd( + pub fn generate_evaluations_from_total_plmc( &self, - usd_amount: Balance, + total_plmc_amount: Balance, // This is the total PLMC to be distributed evaluations_count: u8, ) -> Vec> { - // Even distribution of weights totaling 100% among bids. - let weights = { - if evaluations_count == 0 { - return vec![]; - } - let base = 100 / evaluations_count; - let remainder = 100 % evaluations_count; - let mut result = vec![base; evaluations_count as usize]; - for i in 0..remainder { - result[i as usize] += 1; - } - result - }; + if evaluations_count == 0 { + return vec![]; + } - let evaluators = (0..evaluations_count as u32).map(|i| self.account_from_u32(i, "EVALUATOR")).collect_vec(); - zip(evaluators, weights) - .map(|(evaluator, weight)| { - let ticket_size = Percent::from_percent(weight) * usd_amount; - (evaluator, ticket_size).into() - }) - .collect() + let mut evaluations = Vec::with_capacity(evaluations_count as usize); + let base_weight = 100 / evaluations_count; + let remainder = 100 % evaluations_count; + + for i in 0..evaluations_count { + let evaluator_account = self.account_from_u32(i as u32, "EVALUATOR"); + let weight_for_evaluator = base_weight + if i < remainder { 1 } else { 0 }; + + // Calculate PLMC amount for this evaluator based on weight + let plmc_for_evaluator = Percent::from_percent(weight_for_evaluator) * total_plmc_amount; + + evaluations.push(EvaluationParams::from((evaluator_account, plmc_for_evaluator))); + } + + evaluations } pub fn generate_successful_evaluations( @@ -386,12 +374,23 @@ impl< project_metadata: ProjectMetadataOf, evaluations_count: u8, ) -> Vec> { - let funding_target = project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size); + let funding_target_usd = + project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size); // if we use just the threshold, then for big usd targets we lose the evaluation due to PLMC conversion errors in `evaluation_end` - let evaluation_success_threshold = 100; - let usd_threshold = Percent::from_percent(evaluation_success_threshold) * funding_target; + let target_usd_for_success = Percent::from_percent(100) * funding_target_usd; - self.generate_evaluations_from_total_usd(usd_threshold, evaluations_count) + let plmc_usd_price = >::get_decimals_aware_price(&Location::here(), PLMC_DECIMALS).unwrap(); + // We want to find PLMC amount such that: PLMC_amount * price_of_plmc_in_usd = target_usd_for_success + // So, PLMC_amount = target_usd_for_success / price_of_plmc_in_usd + // Which is target_usd_for_success * (1 / price_of_plmc_in_usd) + let price_reciprocal = + plmc_usd_price.reciprocal().expect("Price reciprocal failed in test; price cannot be zero"); + + let total_plmc_for_success = price_reciprocal + .checked_mul_int(target_usd_for_success) + .expect("Failed to calculate total PLMC for success in test (multiplication error)"); + + self.generate_evaluations_from_total_plmc(total_plmc_for_success, evaluations_count) } pub fn generate_failing_evaluations( @@ -404,8 +403,15 @@ impl< let evaluation_fail_percent = ::EvaluationSuccessThreshold::get().deconstruct() / 2; let usd_threshold = Percent::from_percent(evaluation_fail_percent) * funding_target; + let plmc_usd_price = >::get_decimals_aware_price(&Location::here(), PLMC_DECIMALS).unwrap(); + let price_reciprocal = + plmc_usd_price.reciprocal().expect("Price reciprocal failed in test; price cannot be zero"); + + let total_plmc_for_failure = price_reciprocal + .checked_mul_int(usd_threshold) + .expect("Failed to calculate total PLMC for failure in test (multiplication error)"); - self.generate_evaluations_from_total_usd(usd_threshold, evaluations_count) + self.generate_evaluations_from_total_plmc(total_plmc_for_failure, evaluations_count) } pub fn generate_bids_from_total_ct_amount(&self, bids_count: u32, total_ct_bid: Balance) -> Vec> { diff --git a/pallets/funding/src/instantiator/chain_interactions.rs b/pallets/funding/src/instantiator/chain_interactions.rs index 0a43e0cc3..2596b1aaa 100644 --- a/pallets/funding/src/instantiator/chain_interactions.rs +++ b/pallets/funding/src/instantiator/chain_interactions.rs @@ -410,12 +410,12 @@ impl< pub fn evaluate_for_users(&mut self, project_id: ProjectId, bonds: Vec>) -> DispatchResult { let project_policy = self.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); - for EvaluationParams { account, usd_amount, receiving_account } in bonds { + for EvaluationParams { account, plmc_amount, receiving_account } in bonds { self.execute(|| { crate::Pallet::::do_evaluate( &account.clone(), project_id, - usd_amount, + plmc_amount, generate_did_from_account(account.clone()), project_policy.clone(), receiving_account, diff --git a/pallets/funding/src/instantiator/mod.rs b/pallets/funding/src/instantiator/mod.rs index 2bb9efc6d..4fc437bcd 100644 --- a/pallets/funding/src/instantiator/mod.rs +++ b/pallets/funding/src/instantiator/mod.rs @@ -18,7 +18,7 @@ extern crate alloc; use crate::{traits::*, *}; use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; -use core::{cell::RefCell, iter::zip, marker::PhantomData}; +use core::{cell::RefCell, marker::PhantomData}; use frame_support::{ pallet_prelude::*, traits::{ diff --git a/pallets/funding/src/instantiator/types.rs b/pallets/funding/src/instantiator/types.rs index e81e4826c..06cfc4b79 100644 --- a/pallets/funding/src/instantiator/types.rs +++ b/pallets/funding/src/instantiator/types.rs @@ -97,37 +97,33 @@ impl Total for Vec> { #[serde(rename_all = "camelCase", deny_unknown_fields, bound(serialize = ""), bound(deserialize = ""))] pub struct EvaluationParams { pub account: AccountIdOf, - pub usd_amount: Balance, + pub plmc_amount: Balance, pub receiving_account: Junction, } impl EvaluationParams { - pub const fn new(account: AccountIdOf, usd_amount: Balance, receiving_account: Junction) -> Self { - EvaluationParams:: { account, usd_amount, receiving_account } + pub const fn new(account: AccountIdOf, plmc_amount: Balance, receiving_account: Junction) -> Self { + EvaluationParams:: { account, plmc_amount, receiving_account } } } impl From<(AccountIdOf, Balance, Junction)> for EvaluationParams { - fn from((account, usd_amount, receiving_account): (AccountIdOf, Balance, Junction)) -> Self { - EvaluationParams::::new(account, usd_amount, receiving_account) + fn from((account, plmc_amount, receiving_account): (AccountIdOf, Balance, Junction)) -> Self { + EvaluationParams::::new(account, plmc_amount, receiving_account) } } impl From<(AccountIdOf, Balance)> for EvaluationParams { - fn from((account, usd_amount): (AccountIdOf, Balance)) -> Self { + fn from((account, plmc_amount): (AccountIdOf, Balance)) -> Self { let receiving_account = Junction::AccountId32 { network: Some(NetworkId::Polkadot), id: T::AccountId32Conversion::convert(account.clone()), }; - EvaluationParams::::new(account, usd_amount, receiving_account) + EvaluationParams::::new(account, plmc_amount, receiving_account) } } impl Accounts for Vec> { type Account = AccountIdOf; fn accounts(&self) -> Vec { - let mut btree = BTreeSet::new(); - for EvaluationParams { account, usd_amount: _, receiving_account: _ } in self { - btree.insert(account.clone()); - } - btree.into_iter().collect_vec() + self.iter().map(|params| params.account.clone()).collect::>().into_iter().collect_vec() } } diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index bd6463907..f38ad1567 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -136,6 +136,7 @@ pub type VestingOf = pallet_linear_release::Pallet; pub type BlockNumberToBalanceOf = ::BlockNumberToBalance; pub type RuntimeHoldReasonOf = ::RuntimeHoldReason; pub type PriceProviderOf = ::PriceProvider; +pub type BlockProviderFor = ::BlockNumberProvider; pub type BlockNumberFor = <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; #[frame_support::pallet] @@ -683,7 +684,7 @@ pub mod pallet { origin: OriginFor, jwt: UntrustedToken, project_id: ProjectId, - #[pallet::compact] usd_amount: Balance, + #[pallet::compact] plmc_bond: Balance, ) -> DispatchResult { let (account, did, _investor_type, whitelisted_policy) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; @@ -693,7 +694,7 @@ pub mod pallet { id: T::AccountId32Conversion::convert(account.clone()), }; - Self::do_evaluate(&account, project_id, usd_amount, did, whitelisted_policy, receiving_account) + Self::do_evaluate(&account, project_id, plmc_bond, did, whitelisted_policy, receiving_account) } #[pallet::call_index(5)] @@ -702,7 +703,7 @@ pub mod pallet { origin: OriginFor, jwt: UntrustedToken, project_id: ProjectId, - #[pallet::compact] usd_amount: Balance, + #[pallet::compact] plmc_bond: Balance, receiving_account: Junction, signature_bytes: [u8; 65], ) -> DispatchResult { @@ -711,7 +712,7 @@ pub mod pallet { Self::verify_receiving_account_signature(&account, project_id, &receiving_account, signature_bytes)?; - Self::do_evaluate(&account, project_id, usd_amount, did, whitelisted_policy, receiving_account) + Self::do_evaluate(&account, project_id, plmc_bond, did, whitelisted_policy, receiving_account) } #[pallet::call_index(6)] diff --git a/pallets/funding/src/mock.rs b/pallets/funding/src/mock.rs index 47ea39cad..6b999b3d4 100644 --- a/pallets/funding/src/mock.rs +++ b/pallets/funding/src/mock.rs @@ -279,8 +279,6 @@ impl pallet_timestamp::Config for TestRuntime { type WeightInfo = (); } -pub const HOURS: BlockNumber = 300u64; - // REMARK: In the production configuration we use DAYS instead of HOURS. // We need all durations to use different times to catch bugs in the tests. parameter_types! { diff --git a/pallets/funding/src/tests/2_evaluation.rs b/pallets/funding/src/tests/2_evaluation.rs index d772a42be..3ff41d0ef 100644 --- a/pallets/funding/src/tests/2_evaluation.rs +++ b/pallets/funding/src/tests/2_evaluation.rs @@ -38,7 +38,7 @@ mod round_flow { let target_funding = project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size); let target_evaluation_usd = Percent::from_percent(10) * target_funding; - let evaluations = vec![(EVALUATOR_1, target_evaluation_usd).into()]; + let evaluations = vec![(EVALUATOR_1, 60_000 * PLMC_UNIT).into()]; let evaluation_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone()); inst.mint_plmc_ed_if_required(evaluations.accounts()); @@ -87,7 +87,7 @@ mod round_flow { ::PriceProvider::get_decimals_aware_price(&Location::here(), PLMC_DECIMALS).unwrap() }); let min_evaluation_amount_plmc = - usable_plmc_price.reciprocal().unwrap().checked_mul_int(min_evaluation_amount_usd).unwrap(); + usable_plmc_price.reciprocal().unwrap().checked_mul_int(min_evaluation_amount_usd).unwrap() + 1; // Test independent of CT decimals - Right PLMC conversion is stored. // We move comma 4 places to the left since PLMC has 4 more decimals than USD. @@ -145,14 +145,11 @@ mod round_flow { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - min_evaluation_amount_usd + min_evaluation_amount_plmc ))); // Try bonding up to the threshold with a second evaluation - inst.mint_plmc_to(vec![UserToPLMCBalance::new( - EVALUATOR_2, - evaluation_threshold_plmc + ed - min_evaluation_amount_plmc, - )]); + inst.mint_plmc_to(vec![UserToPLMCBalance::new(EVALUATOR_2, 200_000 * PLMC_UNIT + ed)]); assert_ok!(inst.execute(|| PolimecFunding::evaluate( RuntimeOrigin::signed(EVALUATOR_2), get_mock_jwt_with_cid( @@ -162,7 +159,7 @@ mod round_flow { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluation_threshold_usd - min_evaluation_amount_usd + 200_000 * PLMC_UNIT, ))); // The evaluation should succeed when we bond the threshold PLMC amount in total. @@ -442,9 +439,9 @@ mod evaluate_extrinsic { let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); let evaluations = vec![ - (EVALUATOR_1, 500 * USD_UNIT).into(), - (EVALUATOR_2, 1000 * USD_UNIT).into(), - (EVALUATOR_3, 20_000 * USD_UNIT).into(), + (EVALUATOR_1, 500 * PLMC_UNIT).into(), + (EVALUATOR_2, 1000 * PLMC_UNIT).into(), + (EVALUATOR_3, 20_000 * PLMC_UNIT).into(), ]; inst.mint_necessary_tokens_for_evaluations(evaluations.clone()); @@ -458,7 +455,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluations[0].usd_amount, + evaluations[0].plmc_amount, ))); assert_ok!(inst.execute(|| PolimecFunding::evaluate( @@ -470,7 +467,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluations[1].usd_amount, + evaluations[1].plmc_amount, ))); assert_ok!(inst.execute(|| PolimecFunding::evaluate( @@ -482,7 +479,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluations[2].usd_amount, + evaluations[2].plmc_amount, ))); } @@ -493,7 +490,7 @@ mod evaluate_extrinsic { let project_metadata = default_project_metadata(issuer); let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - let evaluation = EvaluationParams::from((EVALUATOR_1, 500 * USD_UNIT)); + let evaluation = EvaluationParams::from((EVALUATOR_1, 500 * PLMC_UNIT)); let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()]); inst.mint_plmc_ed_if_required(necessary_plmc.accounts()); @@ -512,7 +509,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluation.usd_amount + evaluation.plmc_amount ))); } @@ -521,7 +518,7 @@ mod evaluate_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); - let evaluation = EvaluationParams::from((EVALUATOR_1, 500 * USD_UNIT)); + let evaluation = EvaluationParams::from((EVALUATOR_1, 500 * PLMC_UNIT)); let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()]); let plmc_existential_deposits = necessary_plmc.accounts().existential_deposits(); @@ -543,9 +540,13 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluation.usd_amount + evaluation.plmc_amount ))); + let plmc_usd_price = + >::get_decimals_aware_price(&Location::here(), PLMC_DECIMALS).unwrap(); + let derived_usd_amount = plmc_usd_price.checked_mul_int(necessary_plmc[0].plmc_amount).unwrap(); + inst.execute(|| { let evaluations = Evaluations::::iter_prefix_values((project_id,)).collect_vec(); assert_eq!(evaluations.len(), 1); @@ -557,7 +558,7 @@ mod evaluate_extrinsic { evaluator: EVALUATOR_1, original_plmc_bond: necessary_plmc[0].plmc_amount, current_plmc_bond: necessary_plmc[0].plmc_amount, - early_usd_amount: evaluation.usd_amount, + early_usd_amount: derived_usd_amount, late_usd_amount: 0, when: 1, receiving_account: polkadot_junction!(EVALUATOR_1), @@ -572,7 +573,7 @@ mod evaluate_extrinsic { let issuer = ISSUER_1; let project_metadata = default_project_metadata(issuer); - let evaluation = EvaluationParams::from((EVALUATOR_4, 1_000_000 * USD_UNIT)); + let evaluation = EvaluationParams::from((EVALUATOR_4, 1_000_000 * PLMC_UNIT)); let plmc_required = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()]); let frozen_amount = plmc_required[0].plmc_amount; @@ -601,7 +602,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluation.usd_amount + evaluation.plmc_amount )); }); @@ -676,7 +677,7 @@ mod evaluate_extrinsic { let (eth_acc, eth_sig) = inst.eth_key_and_sig_from("//EVALUATOR1", project_id, EVALUATOR_1); let plmc = - inst.calculate_evaluation_plmc_spent(vec![EvaluationParams::from((EVALUATOR_1, 500 * USD_UNIT))]); + inst.calculate_evaluation_plmc_spent(vec![EvaluationParams::from((EVALUATOR_1, 500 * PLMC_UNIT))]); inst.mint_plmc_ed_if_required(plmc.accounts()); inst.mint_plmc_to(plmc.clone()); @@ -685,7 +686,7 @@ mod evaluate_extrinsic { RuntimeOrigin::signed(EVALUATOR_1), jwt, project_id, - 500 * USD_UNIT, + 500 * PLMC_UNIT, eth_acc, eth_sig, ) @@ -708,7 +709,7 @@ mod evaluate_extrinsic { let (dot_acc, dot_sig) = inst.dot_key_and_sig_from("//EVALUATOR1", project_id, EVALUATOR_1); let plmc = - inst.calculate_evaluation_plmc_spent(vec![EvaluationParams::from((EVALUATOR_1, 500 * USD_UNIT))]); + inst.calculate_evaluation_plmc_spent(vec![EvaluationParams::from((EVALUATOR_1, 500 * PLMC_UNIT))]); inst.mint_plmc_ed_if_required(plmc.accounts()); inst.mint_plmc_to(plmc.clone()); @@ -717,7 +718,7 @@ mod evaluate_extrinsic { RuntimeOrigin::signed(EVALUATOR_1), jwt, project_id, - 500 * USD_UNIT, + 500 * PLMC_UNIT, dot_acc, dot_sig, ) @@ -752,7 +753,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - 500 * USD_UNIT, + 500 * PLMC_UNIT, ), Error::::IncorrectRound ); @@ -787,7 +788,7 @@ mod evaluate_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let issuer = ISSUER_1; let project_metadata = default_project_metadata(issuer); - let evaluations = vec![EvaluationParams::from((EVALUATOR_1, 1000 * USD_UNIT))]; + let evaluations = vec![EvaluationParams::from((EVALUATOR_1, 1000 * PLMC_UNIT))]; let evaluating_plmc = inst.calculate_evaluation_plmc_spent(evaluations.clone()); let mut plmc_insufficient_existential_deposit = evaluating_plmc.accounts().existential_deposits(); @@ -809,7 +810,7 @@ mod evaluate_extrinsic { let project_metadata = default_project_metadata(issuer); let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - let evaluation = EvaluationParams::from((EVALUATOR_1, 500 * USD_UNIT)); + let evaluation = EvaluationParams::from((EVALUATOR_1, 500 * PLMC_UNIT)); let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()]); let ed = necessary_plmc.accounts().existential_deposits(); @@ -836,7 +837,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluation.usd_amount, + evaluation.plmc_amount, ), TokenError::FundsUnavailable ); @@ -853,7 +854,7 @@ mod evaluate_extrinsic { inst.execute(|| crate::Pallet::::do_evaluate( &(&ISSUER_1 + 1), project_id, - 500 * USD_UNIT, + 500 * PLMC_UNIT, generate_did_from_account(ISSUER_1), project_metadata.clone().policy_ipfs_cid.unwrap(), polkadot_junction!(ISSUER_1 + 1) @@ -869,7 +870,7 @@ mod evaluate_extrinsic { let project_metadata = default_project_metadata(issuer); let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer, None); - let evaluation = EvaluationParams::from((EVALUATOR_1, 500 * USD_UNIT)); + let evaluation = EvaluationParams::from((EVALUATOR_1, 500 * PLMC_UNIT)); let necessary_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()]); inst.mint_plmc_ed_if_required(necessary_plmc.accounts()); @@ -885,7 +886,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluation.usd_amount, + evaluation.plmc_amount, )); }); @@ -900,7 +901,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - evaluation.usd_amount, + evaluation.plmc_amount, ), TokenError::FundsUnavailable ); @@ -938,7 +939,7 @@ mod evaluate_extrinsic { RuntimeOrigin::signed(evaluator), jwt.clone(), project_id, - 99 * USD_UNIT + 10 * PLMC_UNIT ), Error::::TooLow ); @@ -962,7 +963,7 @@ mod evaluate_extrinsic { "wrong_cid".as_bytes().to_vec().try_into().unwrap() ), project_id, - 500 * USD_UNIT, + 500 * PLMC_UNIT, ), Error::::PolicyMismatch ); @@ -990,7 +991,7 @@ mod evaluate_extrinsic { project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, - 500 * USD_UNIT, + 500 * PLMC_UNIT, ), Error::::IncorrectRound ); diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index 61b79f55c..b7ba432f2 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -326,7 +326,7 @@ mod bid_extrinsic { let mut evaluations = inst.generate_successful_evaluations(project_metadata.clone(), 5); let evaluator_bidder = 69u64; - let evaluation_amount = 420 * USD_UNIT; + let evaluation_amount = 420 * PLMC_UNIT; let evaluator_bid = BidParams::from(( evaluator_bidder, Retail, @@ -1281,7 +1281,7 @@ mod bid_extrinsic { let project_metadata = default_project_metadata(issuer); let mut evaluations = inst.generate_successful_evaluations(project_metadata.clone(), 5); let evaluator_bidder = 69; - let evaluation_amount = 420 * USD_UNIT; + let evaluation_amount = 420 * PLMC_UNIT; let evaluator_bid = BidParams::from(( evaluator_bidder, Retail, @@ -1316,7 +1316,7 @@ mod bid_extrinsic { let evaluations_2 = evaluations_1.clone(); let evaluator_bidder = 69; - let evaluation_amount = 420 * USD_UNIT; + let evaluation_amount = 420 * PLMC_UNIT; let evaluator_bid = BidParams::from(( evaluator_bidder, Retail, diff --git a/pallets/funding/src/tests/4_funding_end.rs b/pallets/funding/src/tests/4_funding_end.rs index 7beb47211..9a070b887 100644 --- a/pallets/funding/src/tests/4_funding_end.rs +++ b/pallets/funding/src/tests/4_funding_end.rs @@ -78,22 +78,17 @@ mod end_funding_extrinsic { fn evaluator_outcome_bounds() { let try_for_percentage = |percentage: u8, should_slash: bool| { let (mut inst, project_id) = create_project_with_funding_percentage(percentage.into(), true); + let project_details = inst.get_project_details(project_id); if should_slash { + assert_eq!(project_details.status, ProjectStatus::SettlementStarted(FundingOutcome::Failure)); assert_eq!( - inst.get_project_details(project_id).status, - ProjectStatus::SettlementStarted(FundingOutcome::Failure) - ); - assert_eq!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, + project_details.evaluation_round_info.evaluators_outcome, Some(EvaluatorsOutcome::Slashed) ); } else { - assert_eq!( - inst.get_project_details(project_id).status, - ProjectStatus::SettlementStarted(FundingOutcome::Success) - ); + assert_eq!(project_details.status, ProjectStatus::SettlementStarted(FundingOutcome::Success)); assert!(matches!( - inst.get_project_details(project_id).evaluation_round_info.evaluators_outcome, + project_details.evaluation_round_info.evaluators_outcome, Some(EvaluatorsOutcome::Rewarded(..)) )); } diff --git a/pallets/funding/src/tests/5_settlement.rs b/pallets/funding/src/tests/5_settlement.rs index 4ed893766..b2d71708f 100644 --- a/pallets/funding/src/tests/5_settlement.rs +++ b/pallets/funding/src/tests/5_settlement.rs @@ -53,17 +53,17 @@ mod round_flow { let evaluations = vec![ EvaluationParams::from(( EVALUATOR_1, - 500_000 * USD_UNIT, + 500_000 * PLMC_UNIT, Junction::AccountKey20 { network: Some(Ethereum { chain_id: 1 }), key: [0u8; 20] }, )), EvaluationParams::from(( EVALUATOR_2, - 250_000 * USD_UNIT, + 250_000 * PLMC_UNIT, Junction::AccountKey20 { network: Some(Ethereum { chain_id: 1 }), key: [1u8; 20] }, )), EvaluationParams::from(( EVALUATOR_3, - 300_000 * USD_UNIT, + 300_000 * PLMC_UNIT, Junction::AccountKey20 { network: Some(Ethereum { chain_id: 1 }), key: [2u8; 20] }, )), ]; @@ -120,9 +120,9 @@ mod round_flow { project_metadata.minimum_price = decimal_aware_price; let evaluations = vec![ - EvaluationParams::from((EVALUATOR_1, 500_000 * USD_UNIT, polkadot_junction!(EVALUATOR_1 + 420))), - EvaluationParams::from((EVALUATOR_2, 250_000 * USD_UNIT, polkadot_junction!([1u8; 32]))), - EvaluationParams::from((EVALUATOR_3, 300_000 * USD_UNIT, polkadot_junction!([2u8; 32]))), + EvaluationParams::from((EVALUATOR_1, 500_000 * PLMC_UNIT, polkadot_junction!(EVALUATOR_1 + 420))), + EvaluationParams::from((EVALUATOR_2, 250_000 * PLMC_UNIT, polkadot_junction!([1u8; 32]))), + EvaluationParams::from((EVALUATOR_3, 300_000 * PLMC_UNIT, polkadot_junction!([2u8; 32]))), ]; let bids = vec![ BidParams::from(( @@ -255,14 +255,23 @@ mod settle_evaluation_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let mut project_metadata = default_project_metadata(ISSUER_1); project_metadata.total_allocation_size = 1_000_000 * CT_UNIT; + let plmc_usd_price = + >::get_decimals_aware_price(&Location::here(), PLMC_DECIMALS).unwrap(); + let price_reciprocal = plmc_usd_price.reciprocal().unwrap(); + let eval1_target_usd = 500_000 * USD_UNIT; + let eval2_target_usd = 250_000 * USD_UNIT; + let eval3_target_usd = 320_000 * USD_UNIT; + let eval1_plmc_to_bond = price_reciprocal.checked_mul_int(eval1_target_usd).unwrap(); + let eval2_plmc_to_bond = price_reciprocal.checked_mul_int(eval2_target_usd).unwrap(); + let eval3_plmc_to_bond = price_reciprocal.checked_mul_int(eval3_target_usd).unwrap(); let project_id = inst.create_finished_project( project_metadata.clone(), ISSUER_1, None, vec![ - EvaluationParams::from((EVALUATOR_1, 500_000 * USD_UNIT)), - EvaluationParams::from((EVALUATOR_2, 250_000 * USD_UNIT)), - EvaluationParams::from((EVALUATOR_3, 320_000 * USD_UNIT)), + EvaluationParams::from((EVALUATOR_1, eval1_plmc_to_bond)), + EvaluationParams::from((EVALUATOR_2, eval2_plmc_to_bond)), + EvaluationParams::from((EVALUATOR_3, eval3_plmc_to_bond)), ], inst.generate_bids_from_total_ct_percent(project_metadata.clone(), 100, 30), ); @@ -390,7 +399,7 @@ mod settle_evaluation_extrinsic { fn evaluation_round_failed() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); - let evaluation = EvaluationParams::from((EVALUATOR_1, 1_000 * USD_UNIT)); + let evaluation = EvaluationParams::from((EVALUATOR_1, 1_000 * PLMC_UNIT)); let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); let evaluation_plmc = inst.calculate_evaluation_plmc_spent(vec![evaluation.clone()]); diff --git a/pallets/funding/src/tests/misc.rs b/pallets/funding/src/tests/misc.rs index 259576487..bcad47169 100644 --- a/pallets/funding/src/tests/misc.rs +++ b/pallets/funding/src/tests/misc.rs @@ -62,74 +62,6 @@ mod helper_functions { .unwrap(); assert_eq!(converted_back, original_price); } - - #[test] - fn calculate_evaluation_plmc_spent() { - let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - const EVALUATOR_1: AccountIdOf = 1; - const USD_AMOUNT_1: Balance = 150_000 * USD_UNIT; - const EXPECTED_PLMC_AMOUNT_1: f64 = 17_857.1428571428f64; - - const EVALUATOR_2: AccountIdOf = 2; - const USD_AMOUNT_2: Balance = 50_000 * USD_UNIT; - const EXPECTED_PLMC_AMOUNT_2: f64 = 5_952.3809523809f64; - - const EVALUATOR_3: AccountIdOf = 3; - const USD_AMOUNT_3: Balance = 75_000 * USD_UNIT; - const EXPECTED_PLMC_AMOUNT_3: f64 = 8_928.5714285714f64; - - const EVALUATOR_4: AccountIdOf = 4; - const USD_AMOUNT_4: Balance = 100 * USD_UNIT; - const EXPECTED_PLMC_AMOUNT_4: f64 = 11.9047619047f64; - - const EVALUATOR_5: AccountIdOf = 5; - - // 123.7 USD - const USD_AMOUNT_5: Balance = 1237 * USD_UNIT / 10; - const EXPECTED_PLMC_AMOUNT_5: f64 = 14.7261904761f64; - - const PLMC_PRICE: f64 = 8.4f64; - - assert_eq!( - ::PriceProvider::get_price(&Location::here()).unwrap(), - PriceOf::::from_float(PLMC_PRICE) - ); - - let evaluations = vec![ - EvaluationParams::::from((EVALUATOR_1, USD_AMOUNT_1)), - EvaluationParams::::from((EVALUATOR_2, USD_AMOUNT_2)), - EvaluationParams::::from((EVALUATOR_3, USD_AMOUNT_3)), - EvaluationParams::::from((EVALUATOR_4, USD_AMOUNT_4)), - EvaluationParams::::from((EVALUATOR_5, USD_AMOUNT_5)), - ]; - - let expected_plmc_spent = vec![ - (EVALUATOR_1, EXPECTED_PLMC_AMOUNT_1), - (EVALUATOR_2, EXPECTED_PLMC_AMOUNT_2), - (EVALUATOR_3, EXPECTED_PLMC_AMOUNT_3), - (EVALUATOR_4, EXPECTED_PLMC_AMOUNT_4), - (EVALUATOR_5, EXPECTED_PLMC_AMOUNT_5), - ]; - - let calculated_plmc_spent = inst - .calculate_evaluation_plmc_spent(evaluations) - .into_iter() - .sorted_by(|a, b| a.account.cmp(&b.account)) - .map(|map| map.plmc_amount) - .collect_vec(); - let expected_plmc_spent = expected_plmc_spent - .into_iter() - .sorted_by(|a, b| a.0.cmp(&b.0)) - .map(|map| { - let f64_amount = map.1; - let fixed_amount = FixedU128::from_float(f64_amount); - fixed_amount.checked_mul_int(PLMC).unwrap() - }) - .collect_vec(); - for (expected, calculated) in zip(expected_plmc_spent, calculated_plmc_spent) { - assert_close_enough!(expected, calculated, Perquintill::from_float(0.999)); - } - } } // logic of small functions that extrinsics use to process data or interact with storage diff --git a/pallets/funding/src/tests/mod.rs b/pallets/funding/src/tests/mod.rs index 372d6688f..404c7196c 100644 --- a/pallets/funding/src/tests/mod.rs +++ b/pallets/funding/src/tests/mod.rs @@ -24,15 +24,12 @@ use polimec_common::{ AcceptedFundingAsset, AcceptedFundingAsset::{DOT, ETH, USDC, USDT}, }, - ProvideAssetPrice, USD_DECIMALS, USD_UNIT, + ProvideAssetPrice, PLMC_UNIT, USD_DECIMALS, USD_UNIT, }; use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt, get_mock_jwt_with_cid}; use sp_arithmetic::{traits::Zero, Percent, Perquintill}; use sp_runtime::{bounded_vec, traits::Convert, PerThing, TokenError}; -use std::{ - collections::{BTreeSet, HashSet}, - iter::zip, -}; +use std::collections::{BTreeSet, HashSet}; use InvestorType::{self, *}; #[path = "1_application.rs"] diff --git a/pallets/funding/src/tests/runtime_api.rs b/pallets/funding/src/tests/runtime_api.rs index 75514d8ce..b41dae143 100644 --- a/pallets/funding/src/tests/runtime_api.rs +++ b/pallets/funding/src/tests/runtime_api.rs @@ -4,11 +4,11 @@ use super::*; fn top_evaluations() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let evaluations = vec![ - EvaluationParams::from((EVALUATOR_1, 500_000 * USD_UNIT)), - EvaluationParams::from((EVALUATOR_2, 250_000 * USD_UNIT)), - EvaluationParams::from((EVALUATOR_3, 320_000 * USD_UNIT)), - EvaluationParams::from((EVALUATOR_4, 1_000_000 * USD_UNIT)), - EvaluationParams::from((EVALUATOR_1, 1_000 * USD_UNIT)), + EvaluationParams::from((EVALUATOR_1, 500_000 * PLMC_UNIT)), + EvaluationParams::from((EVALUATOR_2, 250_000 * PLMC_UNIT)), + EvaluationParams::from((EVALUATOR_3, 320_000 * PLMC_UNIT)), + EvaluationParams::from((EVALUATOR_4, 1_000_000 * PLMC_UNIT)), + EvaluationParams::from((EVALUATOR_1, 1_000 * PLMC_UNIT)), ]; let project_id = inst.create_auctioning_project(default_project_metadata(ISSUER_1), ISSUER_1, None, evaluations); @@ -380,7 +380,7 @@ fn get_message_to_sign_by_receiving_account() { #[test] fn get_next_vesting_schedule_merge_candidates() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let evaluations = vec![EvaluationParams::from((EVALUATOR_1, 500_000 * USD_UNIT))]; + let evaluations = vec![EvaluationParams::from((EVALUATOR_1, 500_000 * PLMC_UNIT))]; let bids = vec![ BidParams::from(( BIDDER_1, @@ -631,9 +631,9 @@ fn all_project_participations_by_did() { let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1, None); let evaluations = vec![ - EvaluationParams::from((EVALUATOR_1, 500_000 * USD_UNIT)), + EvaluationParams::from((EVALUATOR_1, 500_000 * PLMC_UNIT)), EvaluationParams::from((EVALUATOR_2, 250_000 * USD_UNIT)), - EvaluationParams::from((EVALUATOR_3, 320_000 * USD_UNIT)), + EvaluationParams::from((EVALUATOR_3, 320_000 * PLMC_UNIT)), ]; let bids = vec![ BidParams::from(( @@ -674,8 +674,13 @@ fn all_project_participations_by_did() { for evaluation in evaluations[1..].to_vec() { let jwt = get_mock_jwt_with_cid(evaluation.account, InvestorType::Retail, did_user.clone(), cid.clone()); inst.execute(|| { - PolimecFunding::evaluate(RuntimeOrigin::signed(evaluation.account), jwt, project_id, evaluation.usd_amount) - .unwrap(); + PolimecFunding::evaluate( + RuntimeOrigin::signed(evaluation.account), + jwt, + project_id, + evaluation.plmc_amount, + ) + .unwrap(); }); } diff --git a/pallets/on-slash-vesting/src/test.rs b/pallets/on-slash-vesting/src/test.rs index a20c4b49b..72365052f 100644 --- a/pallets/on-slash-vesting/src/test.rs +++ b/pallets/on-slash-vesting/src/test.rs @@ -26,7 +26,7 @@ fn one_schedule() { // Slash 30 let _ = >::slash(&MockRuntimeHoldReason::Reason, &1u64, 30u128); - >::on_slash(&1, 30); + >::on_slash(&1, &30); // After calling on_slash, the previously unlocked 20 should be available again assert_eq!(PalletBalances::usable_balance(1), 20); @@ -66,7 +66,7 @@ fn multiple_schedules() { assert_eq!(PalletBalances::usable_balance(1), 200); let _ = >::slash(&MockRuntimeHoldReason::Reason, &1u64, 65u128); - >::on_slash(&1, 65); + >::on_slash(&1, &65); let schedules = >::get(1).unwrap().to_vec(); diff --git a/polimec-common/common/src/lib.rs b/polimec-common/common/src/lib.rs index 6eedc6ba7..04bf9c75c 100644 --- a/polimec-common/common/src/lib.rs +++ b/polimec-common/common/src/lib.rs @@ -251,6 +251,7 @@ pub mod migration_types { pub const USD_DECIMALS: u8 = 6; pub const USD_UNIT: u128 = 10u128.pow(USD_DECIMALS as u32); pub const PLMC_DECIMALS: u8 = 10; +pub const PLMC_UNIT: u128 = 10u128.pow(PLMC_DECIMALS as u32); pub trait ProvideAssetPrice { type AssetId;