From a26d7bc6fa9090b093520ffb6a996e410e35e0f7 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Wed, 14 Jun 2023 14:33:44 +0200 Subject: [PATCH 1/9] feat(209): `BondType` is now `LockType` with 2 variants, `Evaluation` and `Participation`. `merge_mappings_by_user` now can accept any mapping of any length without being pre-sorted. It will sort them and either sum 2 existing users or append them in order. --- .../pallets/funding/src/functions.rs | 24 ++-- polimec-skeleton/pallets/funding/src/lib.rs | 2 +- polimec-skeleton/pallets/funding/src/mock.rs | 4 +- polimec-skeleton/pallets/funding/src/tests.rs | 115 ++++++++++++------ polimec-skeleton/pallets/funding/src/types.rs | 5 +- 5 files changed, 92 insertions(+), 58 deletions(-) diff --git a/polimec-skeleton/pallets/funding/src/functions.rs b/polimec-skeleton/pallets/funding/src/functions.rs index 085b3aeb3..b732f5001 100644 --- a/polimec-skeleton/pallets/funding/src/functions.rs +++ b/polimec-skeleton/pallets/funding/src/functions.rs @@ -763,7 +763,7 @@ impl Pallet { match existing_evaluations.try_push(new_evaluation.clone()) { Ok(_) => { - T::NativeCurrency::hold(&BondType::Evaluation(project_id), &evaluator, plmc_bond) + T::NativeCurrency::hold(&LockType::Evaluation(project_id), &evaluator, plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; } Err(_) => { @@ -777,14 +777,14 @@ impl Pallet { Error::::EvaluationBondTooLow ); T::NativeCurrency::release( - &BondType::Evaluation(project_id), + &LockType::Evaluation(project_id), &lowest_evaluation.evaluator, lowest_evaluation.plmc_bond, Precision::Exact, ) .map_err(|_| Error::::InsufficientBalance)?; - T::NativeCurrency::hold(&BondType::Evaluation(project_id), &evaluator, plmc_bond) + T::NativeCurrency::hold(&LockType::Evaluation(project_id), &evaluator, plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; // This should never fail since we just removed an element from the vector @@ -832,7 +832,7 @@ impl Pallet { // * Update Storage * T::NativeCurrency::release( - &BondType::Evaluation(project_id), + &LockType::Evaluation(project_id), &evaluation.evaluator.clone(), evaluation.plmc_bond, Precision::Exact, @@ -934,7 +934,7 @@ impl Pallet { // * Update storage * match existing_bids.try_push(new_bid.clone()) { Ok(_) => { - T::NativeCurrency::hold(&BondType::Bid(project_id), &bidder, required_plmc_bond) + T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; T::FundingCurrency::transfer( @@ -957,13 +957,13 @@ impl Pallet { ensure!(new_bid.clone() > lowest_bid, Error::::BidTooLow); T::NativeCurrency::release( - &BondType::Bid(project_id), + &LockType::Participation(project_id), &lowest_bid.bidder.clone(), lowest_bid.plmc_bond, Precision::Exact, )?; - T::NativeCurrency::hold(&BondType::Bid(project_id), &bidder, required_plmc_bond) + T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; T::FundingCurrency::transfer( @@ -1107,7 +1107,7 @@ impl Pallet { // Try adding the new contribution to the system match existing_contributions.try_push(new_contribution.clone()) { Ok(_) => { - T::NativeCurrency::hold(&BondType::Contribution(project_id), &contributor, required_plmc_bond) + T::NativeCurrency::hold(&LockType::Participation(project_id), &contributor, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; T::FundingCurrency::transfer( @@ -1136,13 +1136,13 @@ impl Pallet { // 1_585_769_442 // T::NativeCurrency::release( - &BondType::Contribution(project_id), + &LockType::Participation(project_id), &lowest_contribution.contributor, lowest_contribution.plmc_bond, Precision::Exact, )?; - T::NativeCurrency::hold(&BondType::Contribution(project_id), &contributor, required_plmc_bond) + T::NativeCurrency::hold(&LockType::Participation(project_id), &contributor, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; T::FundingCurrency::transfer( @@ -1234,7 +1234,7 @@ impl Pallet { // * Update storage * // TODO: check that the full amount was unreserved - T::NativeCurrency::release(&BondType::Bid(project_id), &bid.bidder, unbond_amount, Precision::Exact)?; + T::NativeCurrency::release(&LockType::Participation(project_id), &bid.bidder, unbond_amount, Precision::Exact)?; new_bids.push(bid.clone()); // * Emit events * @@ -1369,7 +1369,7 @@ impl Pallet { // TODO: Should we mint here, or should the full mint happen to the treasury and then do transfers from there? // Unreserve the funds for the user T::NativeCurrency::release( - &BondType::Contribution(project_id), + &LockType::Participation(project_id), &claimer, unbond_amount, Precision::Exact, diff --git a/polimec-skeleton/pallets/funding/src/lib.rs b/polimec-skeleton/pallets/funding/src/lib.rs index 91003831e..67f547252 100644 --- a/polimec-skeleton/pallets/funding/src/lib.rs +++ b/polimec-skeleton/pallets/funding/src/lib.rs @@ -244,7 +244,7 @@ pub type BidInfoOf = BidInfo< >; pub type ContributionInfoOf = ContributionInfo, ProjectIdOf, AccountIdOf, BalanceOf, VestingOf, VestingOf>; -pub type BondTypeOf = BondType>; +pub type BondTypeOf = LockType>; const PLMC_STATEMINT_ID: u32 = 2069; diff --git a/polimec-skeleton/pallets/funding/src/mock.rs b/polimec-skeleton/pallets/funding/src/mock.rs index 8d9fa92ca..adb149460 100644 --- a/polimec-skeleton/pallets/funding/src/mock.rs +++ b/polimec-skeleton/pallets/funding/src/mock.rs @@ -176,8 +176,8 @@ impl pallet_balances::Config for TestRuntime { type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; - type ReserveIdentifier = BondType; - type HoldIdentifier = BondType; + type ReserveIdentifier = LockType; + type HoldIdentifier = LockType; type FreezeIdentifier = (); type MaxLocks = frame_support::traits::ConstU32<1024>; type MaxReserves = frame_support::traits::ConstU32<1024>; diff --git a/polimec-skeleton/pallets/funding/src/tests.rs b/polimec-skeleton/pallets/funding/src/tests.rs index 82a156a3c..8d0b3eca2 100644 --- a/polimec-skeleton/pallets/funding/src/tests.rs +++ b/polimec-skeleton/pallets/funding/src/tests.rs @@ -347,7 +347,7 @@ impl TestEnvironment { .execute_with(|| ::NativeCurrency::total_issuance()) } fn do_reserved_plmc_assertions( - &self, correct_funds: UserToPLMCBalance, reserve_type: BondType>, + &self, correct_funds: UserToPLMCBalance, reserve_type: LockType>, ) { for (user, balance) in correct_funds { self.ext_env.borrow_mut().execute_with(|| { @@ -357,7 +357,7 @@ impl TestEnvironment { } } #[allow(dead_code)] - fn get_reserved_fundings(&self, reserve_type: BondType>) -> UserToPLMCBalance { + fn get_reserved_fundings(&self, reserve_type: LockType>) -> UserToPLMCBalance { self.ext_env.borrow_mut().execute_with(|| { let mut fundings = UserToPLMCBalance::new(); let user_keys: Vec = frame_system::Account::::iter_keys().collect(); @@ -571,7 +571,7 @@ impl<'a> EvaluatingProject<'a> { test_env.do_free_plmc_assertions(expected_free_plmc_balances); test_env.do_reserved_plmc_assertions( expected_reserved_plmc_balances, - BondType::Evaluation(self.get_project_id()), + LockType::Evaluation(self.get_project_id()), ); test_env.do_total_plmc_assertions(total_plmc_supply); } @@ -783,7 +783,7 @@ impl<'a> CommunityFundingProject<'a> { auctioning_project.bid_for_users(bids).expect("Bidding should work"); - test_env.do_reserved_plmc_assertions(plmc_bid_deposits, BondType::Bid(project_id)); + test_env.do_reserved_plmc_assertions(plmc_bid_deposits, LockType::Participation(project_id)); test_env.do_bid_transferred_statemint_asset_assertions(funding_asset_deposits, project_id); test_env.do_free_plmc_assertions(expected_free_plmc_balances); test_env.do_free_statemint_asset_assertions(prev_funding_asset_balances); @@ -934,7 +934,7 @@ impl<'a> RemainderFundingProject<'a> { .buy_for_retail_users(contributions.clone()) .expect("Contributing should work"); - test_env.do_reserved_plmc_assertions(plmc_contribution_deposits, BondType::Contribution(project_id)); + test_env.do_reserved_plmc_assertions(plmc_contribution_deposits, LockType::Participation(project_id)); test_env.do_contribution_transferred_statemint_asset_assertions(funding_asset_deposits, project_id); test_env.do_free_plmc_assertions(expected_free_plmc_balances); test_env.do_free_statemint_asset_assertions(prev_funding_asset_balances); @@ -1009,7 +1009,10 @@ impl<'a> FinishedProject<'a> { let prev_plmc_balances = test_env.get_free_plmc_balances_for(contributors.clone()); let prev_funding_asset_balances = test_env.get_free_statemint_asset_balances_for(asset_id, contributors.clone()); - let plmc_contribution_deposits = calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); + let plmc_bid_deposits = calculate_auction_plmc_spent(bids.clone()); + let plmc_community_contribution_deposits = calculate_contributed_plmc_spent(community_contributions.clone(), ct_price); + let plmc_remainder_contribution_deposits = calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); + let total_plmc_participation_locked = merge_mappings_by_user(vec![plmc_bid_deposits, plmc_community_contribution_deposits, plmc_remainder_contribution_deposits.clone()]); let plmc_existential_deposits: UserToPLMCBalance = contributors .iter() .map(|acc| (acc.clone(), get_ed())) @@ -1018,7 +1021,7 @@ impl<'a> FinishedProject<'a> { calculate_contributed_funding_asset_spent(remainder_contributions.clone(), ct_price); let contributor_balances = sum_balance_mappings(vec![ - plmc_contribution_deposits.clone(), + plmc_remainder_contribution_deposits.clone(), plmc_existential_deposits.clone(), ]); @@ -1028,7 +1031,7 @@ impl<'a> FinishedProject<'a> { let prev_supply = test_env.get_plmc_total_supply(); let post_supply = prev_supply + contributor_balances; - test_env.mint_plmc_to(plmc_contribution_deposits.clone()); + test_env.mint_plmc_to(plmc_remainder_contribution_deposits.clone()); test_env.mint_plmc_to(plmc_existential_deposits.clone()); test_env.mint_statemint_asset_to(funding_asset_deposits.clone()); @@ -1036,7 +1039,7 @@ impl<'a> FinishedProject<'a> { .buy_for_any_user(remainder_contributions.clone()) .expect("Remainder Contributing should work"); - test_env.do_reserved_plmc_assertions(plmc_contribution_deposits, BondType::Contribution(project_id)); + test_env.do_reserved_plmc_assertions(total_plmc_participation_locked, LockType::Participation(project_id)); test_env.do_contribution_transferred_statemint_asset_assertions(funding_asset_deposits, project_id); test_env.do_free_plmc_assertions(expected_free_plmc_balances); test_env.do_free_statemint_asset_assertions(prev_funding_asset_balances); @@ -1229,17 +1232,49 @@ pub mod helper_functions { output } - pub fn merge_mappings_by_user>( + /// add all the user -> I maps together, and add the I's of the ones with the same user. + pub fn merge_mappings_by_user + Ord + Copy>( mut mappings: Vec, I)>>, ) -> Vec<(AccountIdOf, I)> { let mut output = mappings.swap_remove(0); - for map in mappings { - output = zip(output, map) - .map(|(a, b)| { - assert_eq!(a.0, b.0, "Bad order for users"); - (a.0, a.1 + b.1) - }) - .collect(); + output.sort_by_key(|k| k.0); + for mut map in mappings { + map.sort_by_key(|k| k.0); + let old_output = output.clone(); + output = Vec::new(); + let mut i = 0; + let mut j = 0; + while true { + let old_tup = old_output.get(i); + let new_tup = map.get(j); + + match (old_tup, new_tup) { + (None, None) => break, + (Some(_), None) => { + output.extend_from_slice(&old_output[i..]); + break + }, + (None, Some(_)) => { + output.extend_from_slice(&map[j..]); + break + + } + (Some((acc_i, val_i)), Some((acc_j, val_j))) => { + if acc_i == acc_j { + output.push((acc_i.clone(), val_i.clone() + val_j.clone())); + i += 1; + j += 1; + } else if acc_i < acc_j { + output.push(old_output[i]); + i += 1; + } else { + output.push(map[j]); + j += 1; + } + } + } + } + } output } @@ -1499,7 +1534,7 @@ mod evaluation_round_failure { .expect("Bonding should work"); test_env.do_free_plmc_assertions(plmc_existential_deposits); - test_env.do_reserved_plmc_assertions(plmc_eval_deposits, BondType::Evaluation(project_id)); + test_env.do_reserved_plmc_assertions(plmc_eval_deposits, LockType::Evaluation(project_id)); test_env.advance_time(evaluation_end - now + 1); @@ -2094,7 +2129,7 @@ mod community_round_success { test_env.do_free_plmc_assertions(vec![plmc_fundings[1].clone()]); test_env.do_free_statemint_asset_assertions(vec![(BOB, 0u128, AcceptedFundingAsset::USDT.to_statemint_id())]); - test_env.do_reserved_plmc_assertions(vec![plmc_fundings[0].clone()], BondType::Contribution(project_id)); + test_env.do_reserved_plmc_assertions(vec![plmc_fundings[0].clone()], LockType::Participation(project_id)); test_env.do_contribution_transferred_statemint_asset_assertions( statemint_asset_fundings, community_funding_project.get_project_id(), @@ -2172,7 +2207,7 @@ mod community_round_success { remaining_statemint_assets, AcceptedFundingAsset::USDT.to_statemint_id(), )]); - test_env.do_reserved_plmc_assertions(vec![(BOB, reserved_plmc)], BondType::Contribution(project_id)); + test_env.do_reserved_plmc_assertions(vec![(BOB, reserved_plmc)], LockType::Participation(project_id)); test_env.do_contribution_transferred_statemint_asset_assertions( vec![( BOB, @@ -2228,7 +2263,7 @@ mod community_round_success { assert_eq!(contributor_post_buy_statemint_asset_balance, 0); let plmc_bond_stored = project.in_ext(|| { - ::NativeCurrency::balance_on_hold(&BondType::Contribution(project_id), &CONTRIBUTOR) + ::NativeCurrency::balance_on_hold(&LockType::Participation(project_id), &CONTRIBUTOR) }); let statemint_asset_contributions_stored = project.in_ext(|| { Contributions::::get(project.project_id, CONTRIBUTOR) @@ -2275,7 +2310,7 @@ mod community_round_success { ); let new_plmc_bond_stored = project.in_ext(|| { - ::NativeCurrency::balance_on_hold(&BondType::Contribution(project_id), &CONTRIBUTOR) + ::NativeCurrency::balance_on_hold(&LockType::Participation(project_id), &CONTRIBUTOR) }); let new_statemint_asset_contributions_stored = project.in_ext(|| { Contributions::::get(project.project_id, CONTRIBUTOR) @@ -2341,7 +2376,7 @@ mod community_round_success { assert_eq!(contributor_post_buy_statemint_asset_balance, 0); let plmc_bond_stored = project.in_ext(|| { - ::NativeCurrency::balance_on_hold(&BondType::Contribution(project_id), &CONTRIBUTOR) + ::NativeCurrency::balance_on_hold(&LockType::Participation(project_id), &CONTRIBUTOR) }); let statemint_asset_contributions_stored = project.in_ext(|| { Contributions::::get(project.project_id, CONTRIBUTOR) @@ -2388,7 +2423,7 @@ mod community_round_success { ); let new_plmc_bond_stored = project.in_ext(|| { - ::NativeCurrency::balance_on_hold(&BondType::Contribution(project_id), &CONTRIBUTOR) + ::NativeCurrency::balance_on_hold(&LockType::Participation(project_id), &CONTRIBUTOR) }); let new_statemint_asset_contributions_stored = project.in_ext(|| { Contributions::::get(project.project_id, CONTRIBUTOR) @@ -2490,7 +2525,7 @@ mod purchased_vesting { for contribution in community_contributions { let actual_bonded_plmc = test_env.in_ext(|| { ::NativeCurrency::balance_on_hold( - &BondType::Contribution(project_id), + &LockType::Participation(project_id), &contribution.contributor, ) }); @@ -2509,7 +2544,7 @@ mod purchased_vesting { }); let actual_bonded_plmc = test_env.in_ext(|| { ::NativeCurrency::balance_on_hold( - &BondType::Contribution(project_id), + &LockType::Participation(project_id), &contribution.contributor, ) }); @@ -2561,6 +2596,8 @@ mod bids_vesting { fn plmc_unbonded() { let test_env = TestEnvironment::new(); let bids = default_bids(); + let community_contributions = default_community_buys(); + let remainder_contributions = default_remainder_buys(); let finished_project = FinishedProject::new_with( &test_env, default_project(test_env.get_new_nonce()), @@ -2571,25 +2608,23 @@ mod bids_vesting { default_remainder_buys(), ); let project_id = finished_project.project_id; + let ct_price = finished_project.get_project_details().weighted_average_price.unwrap(); - for bid in bids { - let prev_balance = test_env.in_ext(|| ::NativeCurrency::free_balance(&bid.bidder)); - let bonded_plmc = test_env.in_ext(|| { - ::NativeCurrency::balance_on_hold(&BondType::Bid(project_id), &bid.bidder) - }); - let desired_bond = calculate_auction_plmc_spent(vec![bid.clone()]); - assert_eq!(bonded_plmc, sum_balance_mappings(vec![desired_bond.clone()])); + let plmc_bid_deposits = calculate_auction_plmc_spent(bids.clone()); + let plmc_community_contribution_deposits = calculate_contributed_plmc_spent(community_contributions.clone(), ct_price); + let plmc_remainder_contribution_deposits = calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); + let total_plmc_participation_locked = merge_mappings_by_user(vec![plmc_bid_deposits.clone(), plmc_community_contribution_deposits, plmc_remainder_contribution_deposits.clone()]); + test_env.do_reserved_plmc_assertions(total_plmc_participation_locked.clone(), LockType::Participation(project_id)); + + for (bidder, deposit) in plmc_bid_deposits { + let bidder_participation_locked = total_plmc_participation_locked.clone().into_iter().find(|(acc, _)| acc.clone() == bidder.clone()).unwrap().1; let result = test_env.in_ext(|| { - FundingModule::vested_plmc_bid_unbond_for(RuntimeOrigin::signed(bid.bidder), project_id, bid.bidder) + FundingModule::vested_plmc_bid_unbond_for(RuntimeOrigin::signed(bidder.clone()), project_id, bidder) }); assert_ok!(result); - let new_balance = test_env.in_ext(|| ::NativeCurrency::free_balance(bid.bidder)); - assert_eq!(new_balance, prev_balance + sum_balance_mappings(vec![desired_bond])); - let bonded_plmc = test_env.in_ext(|| { - ::NativeCurrency::balance_on_hold(&BondType::Bid(project_id), &bid.bidder) - }); - assert_eq!(bonded_plmc, 0u32.into()); + + test_env.do_reserved_plmc_assertions(vec![(bidder, bidder_participation_locked - deposit)], LockType::Participation(project_id)); } } } diff --git a/polimec-skeleton/pallets/funding/src/types.rs b/polimec-skeleton/pallets/funding/src/types.rs index d93a4e34d..dd286cf4a 100644 --- a/polimec-skeleton/pallets/funding/src/types.rs +++ b/polimec-skeleton/pallets/funding/src/types.rs @@ -54,10 +54,9 @@ pub mod config_types { /// Enum used to identify PLMC holds #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, Copy, Ord, PartialOrd)] - pub enum BondType { + pub enum LockType { Evaluation(ProjectId), - Bid(ProjectId), - Contribution(ProjectId), + Participation(ProjectId), } pub struct ConstPriceProvider(PhantomData<(AssetId, Price, Mapping)>); From ea6e1cb342537ebb8173e96d22747a5652a90486 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Fri, 16 Jun 2023 14:24:28 +0200 Subject: [PATCH 2/9] wip: checkpoint --- .../pallets/funding/src/functions.rs | 8 +++++ polimec-skeleton/pallets/funding/src/tests.rs | 29 +++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/polimec-skeleton/pallets/funding/src/functions.rs b/polimec-skeleton/pallets/funding/src/functions.rs index b732f5001..819936ffd 100644 --- a/polimec-skeleton/pallets/funding/src/functions.rs +++ b/polimec-skeleton/pallets/funding/src/functions.rs @@ -31,6 +31,7 @@ use frame_support::{ Get, }, }; +use frame_support::traits::fungible::InspectHold; use sp_arithmetic::traits::Zero; use sp_runtime::Percent; @@ -874,6 +875,7 @@ impl Pallet { let now = >::block_number(); let bid_id = Self::next_bid_id(); let mut existing_bids = Bids::::get(project_id, bidder.clone()); + let evaluation_bonded = ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &bidder); let ticket_size = ct_usd_price.checked_mul_int(ct_amount).ok_or(Error::::BadMath)?; let funding_asset_usd_price = @@ -934,6 +936,12 @@ impl Pallet { // * Update storage * match existing_bids.try_push(new_bid.clone()) { Ok(_) => { + let new_bond_to_lock = required_plmc_bond.saturating_sub(evaluation_bonded); + let evaluation_bonded_to_change_lock = required_plmc_bond.saturating_sub(new_bond_to_lock); + + T::NativeCurrency::release(&LockType::Evaluation(project_id), &bidder, evaluation_bonded_to_change_lock, Precision::Exact) + .map_err(|_| Error::::ImpossibleState)?; // should be impossible to fail + T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; diff --git a/polimec-skeleton/pallets/funding/src/tests.rs b/polimec-skeleton/pallets/funding/src/tests.rs index 8d0b3eca2..22dc4c7be 100644 --- a/polimec-skeleton/pallets/funding/src/tests.rs +++ b/polimec-skeleton/pallets/funding/src/tests.rs @@ -1607,6 +1607,31 @@ mod auction_round_success { CommunityFundingProject::new_with(&test_env, project4, issuer, evaluations, bids); } + #[test] + fn evaluation_bond_counts_towards_bid() { + let test_env = TestEnvironment::new(); + let issuer = ISSUER; + let project = default_project(test_env.get_new_nonce()); + let mut evaluations = default_evaluations(); + let evaluator_bidder = 69; + let evaluation_amount = 420 * US_DOLLAR; + let evaluator_bid = TestBid::new(evaluator_bidder, 600 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT); + evaluations.push((evaluator_bidder, evaluation_amount)); + + let bidding_project = + AuctioningProject::new_with(&test_env, project, issuer, evaluations); + + let already_bonded_plmc = calculate_evaluation_plmc_spent(vec![(evaluator_bidder, evaluation_amount)])[0].1; + let necessary_plmc_for_bid = calculate_auction_plmc_spent(vec![evaluator_bid])[0].1; + let necessary_usdt_for_bid = calculate_auction_funding_asset_spent(vec![evaluator_bid]); + + test_env.mint_plmc_to(vec![(evaluator_bidder, necessary_plmc_for_bid - already_bonded_plmc)]); + test_env.mint_statemint_asset_to(necessary_usdt_for_bid); + + bidding_project.bid_for_users(vec![evaluator_bid]).unwrap(); + + } + #[test] fn price_calculation() { // Calculate the weighted price of the token for the next funding rounds, using winning bids. @@ -2645,12 +2670,12 @@ mod misc_features { }); test_env.advance_time(2u64); test_env.ext_env.borrow_mut().execute_with(|| { - let stored = crate::ProjectsToUpdate::::iter_values().collect::>(); + let stored = ProjectsToUpdate::::iter_values().collect::>(); assert_eq!(stored.len(), 3, "There should be 3 blocks scheduled for updating"); FundingModule::remove_from_update_store(&69u32).unwrap(); - let stored = crate::ProjectsToUpdate::::iter_values().collect::>(); + let stored = ProjectsToUpdate::::iter_values().collect::>(); assert_eq!( stored[2], vec![], From 90d03a479eacd75bcb53be31a952642164c46d4e Mon Sep 17 00:00:00 2001 From: Juan Ignacio RIos Date: Thu, 22 Jun 2023 17:18:03 +0200 Subject: [PATCH 3/9] wip(209) --- .../pallets/funding/src/functions.rs | 35 ++- polimec-skeleton/pallets/funding/src/lib.rs | 4 +- polimec-skeleton/pallets/funding/src/tests.rs | 230 ++++++++++++++++-- 3 files changed, 236 insertions(+), 33 deletions(-) diff --git a/polimec-skeleton/pallets/funding/src/functions.rs b/polimec-skeleton/pallets/funding/src/functions.rs index 819936ffd..1ea101b64 100644 --- a/polimec-skeleton/pallets/funding/src/functions.rs +++ b/polimec-skeleton/pallets/funding/src/functions.rs @@ -722,7 +722,7 @@ impl Pallet { Ok(()) } // Note: usd_amount needs to have the same amount of decimals as PLMC,, so when multiplied by the plmc-usd price, it gives us the PLMC amount with the decimals we wanted. - pub fn do_evaluation( + pub fn do_evaluate( evaluator: AccountIdOf, project_id: T::ProjectIdentifier, usd_amount: BalanceOf, ) -> Result<(), DispatchError> { // * Get variables * @@ -940,7 +940,7 @@ impl Pallet { let evaluation_bonded_to_change_lock = required_plmc_bond.saturating_sub(new_bond_to_lock); T::NativeCurrency::release(&LockType::Evaluation(project_id), &bidder, evaluation_bonded_to_change_lock, Precision::Exact) - .map_err(|_| Error::::ImpossibleState)?; // should be impossible to fail + .map_err(|_| Error::::ImpossibleState)?; T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; @@ -964,13 +964,22 @@ impl Pallet { ensure!(new_bid.clone() > lowest_bid, Error::::BidTooLow); - T::NativeCurrency::release( + + let unlocked_bond = T::NativeCurrency::release( &LockType::Participation(project_id), &lowest_bid.bidder.clone(), lowest_bid.plmc_bond, Precision::Exact, )?; + let new_required_plmc_bond = required_plmc_bond.saturating_sub(unlocked_bond); + + let new_bond_to_lock = new_required_plmc_bond.saturating_sub(evaluation_bonded); + let evaluation_bonded_to_change_lock = new_required_plmc_bond.saturating_sub(new_bond_to_lock); + + T::NativeCurrency::release(&LockType::Evaluation(project_id), &bidder, evaluation_bonded_to_change_lock, Precision::Exact) + .map_err(|_| Error::::ImpossibleState)?; + T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; @@ -1035,6 +1044,7 @@ impl Pallet { let now = >::block_number(); let contribution_id = Self::next_contribution_id(); let mut existing_contributions = Contributions::::get(project_id, contributor.clone()); + let evaluation_bonded = ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &contributor); let ct_usd_price = project_details .weighted_average_price @@ -1115,6 +1125,12 @@ impl Pallet { // Try adding the new contribution to the system match existing_contributions.try_push(new_contribution.clone()) { Ok(_) => { + let new_bond_to_lock = required_plmc_bond.saturating_sub(evaluation_bonded); + let evaluation_bonded_to_change_lock = required_plmc_bond.saturating_sub(new_bond_to_lock); + + T::NativeCurrency::release(&LockType::Evaluation(project_id), &contributor, evaluation_bonded_to_change_lock, Precision::Exact) + .map_err(|_| Error::::ImpossibleState)?; + T::NativeCurrency::hold(&LockType::Participation(project_id), &contributor, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; @@ -1140,16 +1156,21 @@ impl Pallet { Error::::ContributionTooLow ); - // _528_589_814 - // 1_585_769_442 - // - T::NativeCurrency::release( + let unlocked_bond = T::NativeCurrency::release( &LockType::Participation(project_id), &lowest_contribution.contributor, lowest_contribution.plmc_bond, Precision::Exact, )?; + let new_required_plmc_bond = required_plmc_bond.saturating_sub(unlocked_bond); + + let new_bond_to_lock = new_required_plmc_bond.saturating_sub(evaluation_bonded); + let evaluation_bonded_to_change_lock = new_required_plmc_bond.saturating_sub(new_bond_to_lock); + + T::NativeCurrency::release(&LockType::Evaluation(project_id), &contributor, evaluation_bonded_to_change_lock, Precision::Exact) + .map_err(|_| Error::::ImpossibleState)?; + T::NativeCurrency::hold(&LockType::Participation(project_id), &contributor, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; diff --git a/polimec-skeleton/pallets/funding/src/lib.rs b/polimec-skeleton/pallets/funding/src/lib.rs index 67f547252..d3da3da87 100644 --- a/polimec-skeleton/pallets/funding/src/lib.rs +++ b/polimec-skeleton/pallets/funding/src/lib.rs @@ -274,7 +274,7 @@ pub mod pallet { /// The inner balance type we will use for all of our outer currency types. (e.g native, funding, CTs) type Balance: Balance + From + FixedPointOperand; - /// Represents the value of something in USD + /// Represents the value of something in USDf type Price: FixedPointNumber + Parameter + Copy; /// The chains native currency @@ -717,7 +717,7 @@ pub mod pallet { origin: OriginFor, project_id: T::ProjectIdentifier, #[pallet::compact] amount: BalanceOf, ) -> DispatchResult { let evaluator = ensure_signed(origin)?; - Self::do_evaluation(evaluator, project_id, amount) + Self::do_evaluate(evaluator, project_id, amount) } /// Release the bonded PLMC for an evaluator if the project assigned to it is in the EvaluationFailed phase diff --git a/polimec-skeleton/pallets/funding/src/tests.rs b/polimec-skeleton/pallets/funding/src/tests.rs index 22dc4c7be..86b3978dd 100644 --- a/polimec-skeleton/pallets/funding/src/tests.rs +++ b/polimec-skeleton/pallets/funding/src/tests.rs @@ -23,6 +23,7 @@ use crate as pallet_funding; use crate::{ mock::{FundingModule, *}, CurrencyMetadata, Error, ParticipantsSize, ProjectMetadata, TicketSize, + traits::{ProvideStatemintPrice} }; use defaults::*; use frame_support::{ @@ -440,22 +441,17 @@ impl TestEnvironment { }); } } + + // Check if a Contribution storage item exists for the given funding asset transfer fn do_contribution_transferred_statemint_asset_assertions( &self, correct_funds: UserToStatemintAsset, project_id: ProjectIdOf, ) { for (user, expected_amount, _token_id) in correct_funds { self.ext_env.borrow_mut().execute_with(|| { - // total amount of contributions for this user for this project stored in the mapping - let contribution_total: ::Balance = - Contributions::::get(project_id, user.clone()) - .iter() - .map(|c| c.funding_asset_amount) - .sum(); - assert_eq!( - contribution_total, expected_amount, - "Wrong statemint asset balance expected for user {}", - user - ); + Contributions::::get(project_id, user.clone()) + .iter() + .find(|c| c.funding_asset_amount == expected_amount) + .expect("Contribution not found in storage"); }); } } @@ -655,7 +651,7 @@ impl<'a> AuctioningProject<'a> { .collect::<_>(); let expected_remaining_plmc: UserToPLMCBalance = - merge_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); + merge_add_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); test_env.mint_plmc_to(plmc_eval_deposits.clone()); test_env.mint_plmc_to(plmc_existential_deposits.clone()); @@ -762,7 +758,7 @@ impl<'a> CommunityFundingProject<'a> { let bidder_balances = sum_balance_mappings(vec![plmc_bid_deposits.clone(), plmc_existential_deposits.clone()]); let expected_free_plmc_balances = - merge_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); + merge_add_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); let prev_supply = test_env.get_plmc_total_supply(); let post_supply = prev_supply + bidder_balances; @@ -921,7 +917,7 @@ impl<'a> RemainderFundingProject<'a> { ]); let expected_free_plmc_balances = - merge_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); + merge_add_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); let prev_supply = test_env.get_plmc_total_supply(); let post_supply = prev_supply + contributor_balances; @@ -991,7 +987,7 @@ impl<'a> FinishedProject<'a> { test_env, project_metadata.clone(), issuer, - evaluations, + evaluations.clone(), bids.clone(), community_contributions.clone(), ); @@ -1009,10 +1005,12 @@ impl<'a> FinishedProject<'a> { let prev_plmc_balances = test_env.get_free_plmc_balances_for(contributors.clone()); let prev_funding_asset_balances = test_env.get_free_statemint_asset_balances_for(asset_id, contributors.clone()); + let plmc_evaluation_deposits = calculate_evaluation_plmc_spent(evaluations.clone()); let plmc_bid_deposits = calculate_auction_plmc_spent(bids.clone()); let plmc_community_contribution_deposits = calculate_contributed_plmc_spent(community_contributions.clone(), ct_price); let plmc_remainder_contribution_deposits = calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); - let total_plmc_participation_locked = merge_mappings_by_user(vec![plmc_bid_deposits, plmc_community_contribution_deposits, plmc_remainder_contribution_deposits.clone()]); + let necessary_plmc_mint = merge_subtract_mappings_by_user(plmc_remainder_contribution_deposits.clone(), vec![plmc_evaluation_deposits]); + let total_plmc_participation_locked = merge_add_mappings_by_user(vec![plmc_bid_deposits, plmc_community_contribution_deposits, plmc_remainder_contribution_deposits.clone()]); let plmc_existential_deposits: UserToPLMCBalance = contributors .iter() .map(|acc| (acc.clone(), get_ed())) @@ -1020,18 +1018,19 @@ impl<'a> FinishedProject<'a> { let funding_asset_deposits = calculate_contributed_funding_asset_spent(remainder_contributions.clone(), ct_price); + let contributor_balances = sum_balance_mappings(vec![ - plmc_remainder_contribution_deposits.clone(), + necessary_plmc_mint.clone(), plmc_existential_deposits.clone(), ]); let expected_free_plmc_balances = - merge_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); + merge_add_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); let prev_supply = test_env.get_plmc_total_supply(); let post_supply = prev_supply + contributor_balances; - test_env.mint_plmc_to(plmc_remainder_contribution_deposits.clone()); + test_env.mint_plmc_to(necessary_plmc_mint.clone()); test_env.mint_plmc_to(plmc_existential_deposits.clone()); test_env.mint_statemint_asset_to(funding_asset_deposits.clone()); @@ -1150,7 +1149,8 @@ mod defaults { pub fn default_remainder_buys() -> TestContributions { vec![ - TestContribution::new(EVALUATOR_2, 6 * ASSET_UNIT, None, AcceptedFundingAsset::USDT), + TestContribution::new(EVALUATOR_2, 30 * ASSET_UNIT, None, AcceptedFundingAsset::USDT), + TestContribution::new(BUYER_2, 6 * ASSET_UNIT, None, AcceptedFundingAsset::USDT), TestContribution::new(BIDDER_1, 4 * ASSET_UNIT, None, AcceptedFundingAsset::USDT), ] } @@ -1163,7 +1163,7 @@ pub mod helper_functions { ::ExistentialDeposit::get() } - pub fn calculate_evaluation_plmc_spent(evals: UserToPLMCBalance) -> UserToPLMCBalance { + pub fn calculate_evaluation_plmc_spent(evals: UserToUSDBalance) -> UserToPLMCBalance { let plmc_price = PriceMap::get().get(&PLMC_STATEMINT_ID).unwrap().clone(); let mut output = UserToPLMCBalance::new(); for eval in evals { @@ -1233,7 +1233,8 @@ pub mod helper_functions { } /// add all the user -> I maps together, and add the I's of the ones with the same user. - pub fn merge_mappings_by_user + Ord + Copy>( + // Mappings should be sorted based on their account id, ascending. + pub fn merge_add_mappings_by_user + Ord + Copy>( mut mappings: Vec, I)>>, ) -> Vec<(AccountIdOf, I)> { let mut output = mappings.swap_remove(0); @@ -1279,6 +1280,54 @@ pub mod helper_functions { output } + // Mappings should be sorted based on their account id, ascending. + pub fn merge_subtract_mappings_by_user( + mut base_mapping: Vec<(AccountIdOf, I)>, + mut subtract_mappings: Vec, I)>>, + ) -> Vec<(AccountIdOf, I)> { + let mut output = base_mapping; + output.sort_by_key(|k| k.0); + for mut map in subtract_mappings { + map.sort_by_key(|k| k.0); + let old_output = output.clone(); + output = Vec::new(); + let mut i = 0; + let mut j = 0; + while true { + let old_tup = old_output.get(i); + let new_tup = map.get(j); + + match (old_tup, new_tup) { + (None, None) => break, + (Some(_), None) => { + output.extend_from_slice(&old_output[i..]); + break + }, + (None, Some(_)) => { + output.extend_from_slice(&map[j..]); + break + + } + (Some((acc_i, val_i)), Some((acc_j, val_j))) => { + if acc_i == acc_j { + output.push((acc_i.clone(), val_i.clone().saturating_sub(val_j.clone()))); + i += 1; + j += 1; + } else if acc_i < acc_j { + output.push(old_output[i]); + i += 1; + } else { + output.push(map[j]); + j += 1; + } + } + } + } + + } + output + } + pub fn sum_balance_mappings + sp_std::iter::Sum>( mut mappings: Vec, I)>>, ) -> I { @@ -1514,7 +1563,7 @@ mod evaluation_round_failure { .map(|(account, _amount)| (account.clone(), get_ed())) .collect::<_>(); let expected_evaluator_balances = - merge_mappings_by_user(vec![plmc_eval_deposits.clone(), plmc_existential_deposits.clone()]); + merge_add_mappings_by_user(vec![plmc_eval_deposits.clone(), plmc_existential_deposits.clone()]); test_env.mint_plmc_to(plmc_eval_deposits.clone()); test_env.mint_plmc_to(plmc_existential_deposits.clone()); @@ -1629,7 +1678,52 @@ mod auction_round_success { test_env.mint_statemint_asset_to(necessary_usdt_for_bid); bidding_project.bid_for_users(vec![evaluator_bid]).unwrap(); + } + + #[test] + fn evaluation_bond_counts_towards_bid_vec_full() { + let test_env = TestEnvironment::new(); + let issuer = ISSUER; + let project = default_project(test_env.get_new_nonce()); + let mut evaluations = default_evaluations(); + let evaluator_bidder = 69; + let evaluator_bid = TestBid::new(evaluator_bidder, 600 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT); + + let mut bids = Vec::new(); + for i in 0..::MaxBidsPerUser::get() { + bids.push(TestBid::new(evaluator_bidder, 10 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT)); + } + + let mut fill_necessary_plmc_for_bids = calculate_auction_plmc_spent(bids.clone()); + let fill_necessary_usdt_for_bids = calculate_auction_funding_asset_spent(bids.clone()); + + let bid_necessary_plmc = calculate_auction_plmc_spent(vec![evaluator_bid]); + let bid_necessary_usdt = calculate_auction_funding_asset_spent(vec![evaluator_bid]); + + let mut evaluation_bond = sum_balance_mappings(vec![fill_necessary_plmc_for_bids, bid_necessary_plmc.clone()]); + const FUNDED_DELTA_PLMC: u128 = 69 * PLMC; + evaluation_bond -= FUNDED_DELTA_PLMC; + + let evaluation_usd_amount = ::PriceProvider::get_price(PLMC_STATEMINT_ID).unwrap().saturating_mul_int(evaluation_bond); + evaluations.push((evaluator_bidder, evaluation_usd_amount)); + let bidding_project = + AuctioningProject::new_with(&test_env, project, issuer, evaluations); + let project_id = bidding_project.get_project_id(); + + test_env.mint_plmc_to(vec![(evaluator_bidder, FUNDED_DELTA_PLMC)]); + test_env.mint_statemint_asset_to(fill_necessary_usdt_for_bids); + test_env.mint_statemint_asset_to(bid_necessary_usdt); + + bidding_project.bid_for_users(bids).unwrap(); + + let evaluation_bond = test_env.in_ext(|| ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &evaluator_bidder)); + let post_fill_evaluation_bond = bid_necessary_plmc[0].1 - FUNDED_DELTA_PLMC; + assert!(evaluation_bond < post_fill_evaluation_bond + 10u128 || evaluation_bond > post_fill_evaluation_bond - 10u128); + + bidding_project.bid_for_users(vec![evaluator_bid]).unwrap(); + let evaluation_bond = test_env.in_ext(|| ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &evaluator_bidder)); + assert_eq!(evaluation_bond, 0); } #[test] @@ -1873,6 +1967,21 @@ mod auction_round_success { }); } } + + #[test] + fn bidder_was_evaluator() { + let test_env = TestEnvironment::new(); + let issuer = ISSUER; + let project = default_project(test_env.get_new_nonce()); + let evaluations = default_evaluations(); + let mut bids = default_bids(); + let evaluator = evaluations[0].0; + bids.push( + TestBid::new(evaluator, 150 * ASSET_UNIT, 21u128.into(), None, AcceptedFundingAsset::USDT) + ); + let _community_funding_project = + CommunityFundingProject::new_with(&test_env, project, issuer, evaluations, bids); + } } #[cfg(test)] @@ -2468,6 +2577,79 @@ mod community_round_success { - statemint_funding[0].1 ); } + + #[test] + fn evaluation_bond_counts_towards_community_contribution() { + let test_env = TestEnvironment::new(); + let issuer = ISSUER; + let project = default_project(test_env.get_new_nonce()); + let mut evaluations = default_evaluations(); + let evaluator_contributor = 69; + let evaluation_amount = 420 * US_DOLLAR; + let contribution = TestContribution::new(evaluator_contributor, 600 * ASSET_UNIT, None, AcceptedFundingAsset::USDT); + evaluations.push((evaluator_contributor, evaluation_amount)); + let bids = default_bids(); + + let contributing_project = + CommunityFundingProject::new_with(&test_env, project, issuer, evaluations, bids); + let ct_price = contributing_project.get_project_details().weighted_average_price.unwrap(); + let already_bonded_plmc = calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount)])[0].1; + let necessary_plmc_for_bid = calculate_contributed_plmc_spent(vec![contribution], ct_price)[0].1; + let necessary_usdt_for_bid = calculate_contributed_funding_asset_spent(vec![contribution], ct_price); + + test_env.mint_plmc_to(vec![(evaluator_contributor, necessary_plmc_for_bid - already_bonded_plmc)]); + test_env.mint_statemint_asset_to(necessary_usdt_for_bid); + + contributing_project.buy_for_retail_users(vec![contribution]).unwrap(); + } + + #[test] + fn evaluation_bond_counts_towards_community_contribution_vec_full() { + let test_env = TestEnvironment::new(); + let issuer = ISSUER; + let project = default_project(test_env.get_new_nonce()); + let mut evaluations = default_evaluations(); + let bids = default_bids(); + let evaluator_contributor = 69; + let overflow_contribution = TestContribution::new(evaluator_contributor, 600 * ASSET_UNIT, None, AcceptedFundingAsset::USDT); + + let mut fill_contributions = Vec::new(); + for i in 0..::MaxContributionsPerUser::get() { + fill_contributions.push(TestContribution::new(evaluator_contributor, 10 * ASSET_UNIT, None, AcceptedFundingAsset::USDT)); + } + + let expected_price = FixedU128::from_float(38.3333333333f64); + let mut fill_necessary_plmc = calculate_contributed_plmc_spent(fill_contributions.clone(), expected_price); + let fill_necessary_usdt_for_bids = calculate_contributed_funding_asset_spent(fill_contributions.clone(), expected_price); + + let overflow_necessary_plmc = calculate_contributed_plmc_spent(vec![overflow_contribution], expected_price); + let overflow_necessary_usdt = calculate_contributed_funding_asset_spent(vec![overflow_contribution], expected_price); + + let mut evaluation_bond = sum_balance_mappings(vec![fill_necessary_plmc, overflow_necessary_plmc.clone()]); + const FUNDED_DELTA_PLMC: u128 = 69 * PLMC; + evaluation_bond -= FUNDED_DELTA_PLMC; + + let evaluation_usd_amount = ::PriceProvider::get_price(PLMC_STATEMINT_ID).unwrap().saturating_mul_int(evaluation_bond); + evaluations.push((evaluator_contributor, evaluation_usd_amount)); + + let community_funding_project = + CommunityFundingProject::new_with(&test_env, project, issuer, evaluations, bids); + let project_id = community_funding_project.get_project_id(); + + test_env.mint_plmc_to(vec![(evaluator_contributor, FUNDED_DELTA_PLMC)]); + test_env.mint_statemint_asset_to(fill_necessary_usdt_for_bids); + test_env.mint_statemint_asset_to(overflow_necessary_usdt); + + community_funding_project.buy_for_retail_users(fill_contributions).unwrap(); + + let evaluation_bond = test_env.in_ext(|| ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &evaluator_contributor)); + let post_fill_evaluation_bond = overflow_necessary_plmc[0].1 - FUNDED_DELTA_PLMC; + assert!(evaluation_bond < post_fill_evaluation_bond + 10u128 || evaluation_bond > post_fill_evaluation_bond - 10u128); + + community_funding_project.buy_for_retail_users(vec![overflow_contribution]).unwrap(); + let evaluation_bond = test_env.in_ext(|| ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &evaluator_contributor)); + assert_eq!(evaluation_bond, 0); + } } #[cfg(test)] @@ -2638,7 +2820,7 @@ mod bids_vesting { let plmc_bid_deposits = calculate_auction_plmc_spent(bids.clone()); let plmc_community_contribution_deposits = calculate_contributed_plmc_spent(community_contributions.clone(), ct_price); let plmc_remainder_contribution_deposits = calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); - let total_plmc_participation_locked = merge_mappings_by_user(vec![plmc_bid_deposits.clone(), plmc_community_contribution_deposits, plmc_remainder_contribution_deposits.clone()]); + let total_plmc_participation_locked = merge_add_mappings_by_user(vec![plmc_bid_deposits.clone(), plmc_community_contribution_deposits, plmc_remainder_contribution_deposits.clone()]); test_env.do_reserved_plmc_assertions(total_plmc_participation_locked.clone(), LockType::Participation(project_id)); From 5562fce3012dc81a169ec433327bd63951096a10 Mon Sep 17 00:00:00 2001 From: Juan Ignacio RIos Date: Fri, 23 Jun 2023 09:55:48 +0200 Subject: [PATCH 4/9] wip(209) --- polimec-skeleton/pallets/funding/src/tests.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/polimec-skeleton/pallets/funding/src/tests.rs b/polimec-skeleton/pallets/funding/src/tests.rs index 86b3978dd..1ef1f014a 100644 --- a/polimec-skeleton/pallets/funding/src/tests.rs +++ b/polimec-skeleton/pallets/funding/src/tests.rs @@ -741,7 +741,7 @@ impl<'a> CommunityFundingProject<'a> { test_env: &'a TestEnvironment, project_metadata: ProjectMetadataOf, issuer: ::AccountId, evaluations: UserToUSDBalance, bids: TestBids, ) -> Self { - let auctioning_project = AuctioningProject::new_with(test_env, project_metadata, issuer, evaluations); + let auctioning_project = AuctioningProject::new_with(test_env, project_metadata, issuer, evaluations.clone()); let project_id = auctioning_project.get_project_id(); let bidders = bids @@ -751,11 +751,14 @@ impl<'a> CommunityFundingProject<'a> { let asset_id = bids[0].asset.to_statemint_id(); let prev_plmc_balances = test_env.get_free_plmc_balances_for(bidders.clone()); let prev_funding_asset_balances = test_env.get_free_statemint_asset_balances_for(asset_id, bidders); + let plmc_evaluation_deposits: UserToPLMCBalance = calculate_evaluation_plmc_spent(evaluations.clone()); let plmc_bid_deposits: UserToPLMCBalance = calculate_auction_plmc_spent(bids.clone()); + let necessary_plmc_mint = merge_subtract_mappings_by_user(plmc_bid_deposits.clone(), vec![plmc_evaluation_deposits]); + let total_plmc_participation_locked = plmc_bid_deposits; let plmc_existential_deposits: UserToPLMCBalance = bids.iter().map(|bid| (bid.bidder, get_ed())).collect::<_>(); let funding_asset_deposits = calculate_auction_funding_asset_spent(bids.clone()); - let bidder_balances = sum_balance_mappings(vec![plmc_bid_deposits.clone(), plmc_existential_deposits.clone()]); + let bidder_balances = sum_balance_mappings(vec![necessary_plmc_mint.clone(), plmc_existential_deposits.clone()]); let expected_free_plmc_balances = merge_add_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); @@ -773,13 +776,13 @@ impl<'a> CommunityFundingProject<'a> { .collect::>(); let total_ct_sold = bids.iter().map(|bid| bid.amount).sum::(); - test_env.mint_plmc_to(plmc_bid_deposits.clone()); + test_env.mint_plmc_to(necessary_plmc_mint.clone()); test_env.mint_plmc_to(plmc_existential_deposits.clone()); test_env.mint_statemint_asset_to(funding_asset_deposits.clone()); auctioning_project.bid_for_users(bids).expect("Bidding should work"); - test_env.do_reserved_plmc_assertions(plmc_bid_deposits, LockType::Participation(project_id)); + test_env.do_reserved_plmc_assertions(total_plmc_participation_locked, LockType::Participation(project_id)); test_env.do_bid_transferred_statemint_asset_assertions(funding_asset_deposits, project_id); test_env.do_free_plmc_assertions(expected_free_plmc_balances); test_env.do_free_statemint_asset_assertions(prev_funding_asset_balances); @@ -904,7 +907,11 @@ impl<'a> RemainderFundingProject<'a> { let prev_plmc_balances = test_env.get_free_plmc_balances_for(contributors.clone()); let prev_funding_asset_balances = test_env.get_free_statemint_asset_balances_for(asset_id, contributors.clone()); + + let plmc_bid_deposits = calculate_auction_plmc_spent(bids.clone()); let plmc_contribution_deposits = calculate_contributed_plmc_spent(contributions.clone(), ct_price); + let necessary_plmc_mint = merge_subtract_mappings_by_user(plmc_contribution_deposits.clone(), vec![plmc_evaluation_deposits]); + let plmc_existential_deposits: UserToPLMCBalance = contributors .iter() .map(|acc| (acc.clone(), get_ed())) From 28d04c638f7b66e395155e235c4e20e174563888 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Fri, 23 Jun 2023 14:43:44 +0200 Subject: [PATCH 5/9] feat(209): new tests for evaluators being bidders or contributors. Existing tests adapted to new logic. --- .../pallets/funding/src/functions.rs | 60 +- polimec-skeleton/pallets/funding/src/tests.rs | 554 ++++++++++++++---- 2 files changed, 482 insertions(+), 132 deletions(-) diff --git a/polimec-skeleton/pallets/funding/src/functions.rs b/polimec-skeleton/pallets/funding/src/functions.rs index 1ea101b64..02c9f7b08 100644 --- a/polimec-skeleton/pallets/funding/src/functions.rs +++ b/polimec-skeleton/pallets/funding/src/functions.rs @@ -21,6 +21,7 @@ use super::*; use crate::traits::{BondingRequirementCalculation, ProvideStatemintPrice}; +use frame_support::traits::fungible::InspectHold; use frame_support::traits::tokens::{Precision, Preservation}; use frame_support::{ ensure, @@ -31,7 +32,6 @@ use frame_support::{ Get, }, }; -use frame_support::traits::fungible::InspectHold; use sp_arithmetic::traits::Zero; use sp_runtime::Percent; @@ -875,7 +875,8 @@ impl Pallet { let now = >::block_number(); let bid_id = Self::next_bid_id(); let mut existing_bids = Bids::::get(project_id, bidder.clone()); - let evaluation_bonded = ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &bidder); + let evaluation_bonded = + ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &bidder); let ticket_size = ct_usd_price.checked_mul_int(ct_amount).ok_or(Error::::BadMath)?; let funding_asset_usd_price = @@ -936,11 +937,16 @@ impl Pallet { // * Update storage * match existing_bids.try_push(new_bid.clone()) { Ok(_) => { - let new_bond_to_lock = required_plmc_bond.saturating_sub(evaluation_bonded); + let new_bond_to_lock = required_plmc_bond.saturating_sub(evaluation_bonded); let evaluation_bonded_to_change_lock = required_plmc_bond.saturating_sub(new_bond_to_lock); - T::NativeCurrency::release(&LockType::Evaluation(project_id), &bidder, evaluation_bonded_to_change_lock, Precision::Exact) - .map_err(|_| Error::::ImpossibleState)?; + T::NativeCurrency::release( + &LockType::Evaluation(project_id), + &bidder, + evaluation_bonded_to_change_lock, + Precision::Exact, + ) + .map_err(|_| Error::::ImpossibleState)?; T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; @@ -964,7 +970,6 @@ impl Pallet { ensure!(new_bid.clone() > lowest_bid, Error::::BidTooLow); - let unlocked_bond = T::NativeCurrency::release( &LockType::Participation(project_id), &lowest_bid.bidder.clone(), @@ -974,11 +979,16 @@ impl Pallet { let new_required_plmc_bond = required_plmc_bond.saturating_sub(unlocked_bond); - let new_bond_to_lock = new_required_plmc_bond.saturating_sub(evaluation_bonded); + let new_bond_to_lock = new_required_plmc_bond.saturating_sub(evaluation_bonded); let evaluation_bonded_to_change_lock = new_required_plmc_bond.saturating_sub(new_bond_to_lock); - T::NativeCurrency::release(&LockType::Evaluation(project_id), &bidder, evaluation_bonded_to_change_lock, Precision::Exact) - .map_err(|_| Error::::ImpossibleState)?; + T::NativeCurrency::release( + &LockType::Evaluation(project_id), + &bidder, + evaluation_bonded_to_change_lock, + Precision::Exact, + ) + .map_err(|_| Error::::ImpossibleState)?; T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; @@ -1044,7 +1054,8 @@ impl Pallet { let now = >::block_number(); let contribution_id = Self::next_contribution_id(); let mut existing_contributions = Contributions::::get(project_id, contributor.clone()); - let evaluation_bonded = ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &contributor); + let evaluation_bonded = + ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &contributor); let ct_usd_price = project_details .weighted_average_price @@ -1125,11 +1136,16 @@ impl Pallet { // Try adding the new contribution to the system match existing_contributions.try_push(new_contribution.clone()) { Ok(_) => { - let new_bond_to_lock = required_plmc_bond.saturating_sub(evaluation_bonded); + let new_bond_to_lock = required_plmc_bond.saturating_sub(evaluation_bonded); let evaluation_bonded_to_change_lock = required_plmc_bond.saturating_sub(new_bond_to_lock); - T::NativeCurrency::release(&LockType::Evaluation(project_id), &contributor, evaluation_bonded_to_change_lock, Precision::Exact) - .map_err(|_| Error::::ImpossibleState)?; + T::NativeCurrency::release( + &LockType::Evaluation(project_id), + &contributor, + evaluation_bonded_to_change_lock, + Precision::Exact, + ) + .map_err(|_| Error::::ImpossibleState)?; T::NativeCurrency::hold(&LockType::Participation(project_id), &contributor, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; @@ -1165,11 +1181,16 @@ impl Pallet { let new_required_plmc_bond = required_plmc_bond.saturating_sub(unlocked_bond); - let new_bond_to_lock = new_required_plmc_bond.saturating_sub(evaluation_bonded); + let new_bond_to_lock = new_required_plmc_bond.saturating_sub(evaluation_bonded); let evaluation_bonded_to_change_lock = new_required_plmc_bond.saturating_sub(new_bond_to_lock); - T::NativeCurrency::release(&LockType::Evaluation(project_id), &contributor, evaluation_bonded_to_change_lock, Precision::Exact) - .map_err(|_| Error::::ImpossibleState)?; + T::NativeCurrency::release( + &LockType::Evaluation(project_id), + &contributor, + evaluation_bonded_to_change_lock, + Precision::Exact, + ) + .map_err(|_| Error::::ImpossibleState)?; T::NativeCurrency::hold(&LockType::Participation(project_id), &contributor, required_plmc_bond) .map_err(|_| Error::::InsufficientBalance)?; @@ -1263,7 +1284,12 @@ impl Pallet { // * Update storage * // TODO: check that the full amount was unreserved - T::NativeCurrency::release(&LockType::Participation(project_id), &bid.bidder, unbond_amount, Precision::Exact)?; + T::NativeCurrency::release( + &LockType::Participation(project_id), + &bid.bidder, + unbond_amount, + Precision::Exact, + )?; new_bids.push(bid.clone()); // * Emit events * diff --git a/polimec-skeleton/pallets/funding/src/tests.rs b/polimec-skeleton/pallets/funding/src/tests.rs index 1ef1f014a..61fd0900d 100644 --- a/polimec-skeleton/pallets/funding/src/tests.rs +++ b/polimec-skeleton/pallets/funding/src/tests.rs @@ -22,8 +22,8 @@ use super::*; use crate as pallet_funding; use crate::{ mock::{FundingModule, *}, + traits::ProvideStatemintPrice, CurrencyMetadata, Error, ParticipantsSize, ProjectMetadata, TicketSize, - traits::{ProvideStatemintPrice} }; use defaults::*; use frame_support::{ @@ -753,12 +753,14 @@ impl<'a> CommunityFundingProject<'a> { let prev_funding_asset_balances = test_env.get_free_statemint_asset_balances_for(asset_id, bidders); let plmc_evaluation_deposits: UserToPLMCBalance = calculate_evaluation_plmc_spent(evaluations.clone()); let plmc_bid_deposits: UserToPLMCBalance = calculate_auction_plmc_spent(bids.clone()); - let necessary_plmc_mint = merge_subtract_mappings_by_user(plmc_bid_deposits.clone(), vec![plmc_evaluation_deposits]); + let necessary_plmc_mint = + merge_subtract_mappings_by_user(plmc_bid_deposits.clone(), vec![plmc_evaluation_deposits]); let total_plmc_participation_locked = plmc_bid_deposits; let plmc_existential_deposits: UserToPLMCBalance = bids.iter().map(|bid| (bid.bidder, get_ed())).collect::<_>(); let funding_asset_deposits = calculate_auction_funding_asset_spent(bids.clone()); - let bidder_balances = sum_balance_mappings(vec![necessary_plmc_mint.clone(), plmc_existential_deposits.clone()]); + let bidder_balances = + sum_balance_mappings(vec![necessary_plmc_mint.clone(), plmc_existential_deposits.clone()]); let expected_free_plmc_balances = merge_add_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); @@ -895,7 +897,7 @@ impl<'a> RemainderFundingProject<'a> { evaluations: UserToUSDBalance, bids: TestBids, contributions: TestContributions, ) -> Self { let community_funding_project = - CommunityFundingProject::new_with(test_env, project_metadata, issuer, evaluations, bids); + CommunityFundingProject::new_with(test_env, project_metadata, issuer, evaluations.clone(), bids.clone()); let project_id = community_funding_project.get_project_id(); let ct_price = community_funding_project @@ -908,20 +910,22 @@ impl<'a> RemainderFundingProject<'a> { let prev_funding_asset_balances = test_env.get_free_statemint_asset_balances_for(asset_id, contributors.clone()); + let plmc_evaluation_deposits = calculate_evaluation_plmc_spent(evaluations.clone()); let plmc_bid_deposits = calculate_auction_plmc_spent(bids.clone()); let plmc_contribution_deposits = calculate_contributed_plmc_spent(contributions.clone(), ct_price); - let necessary_plmc_mint = merge_subtract_mappings_by_user(plmc_contribution_deposits.clone(), vec![plmc_evaluation_deposits]); + let necessary_plmc_mint = + merge_subtract_mappings_by_user(plmc_contribution_deposits.clone(), vec![plmc_evaluation_deposits]); + let total_plmc_participation_locked = + merge_add_mappings_by_user(vec![plmc_bid_deposits, plmc_contribution_deposits.clone()]); let plmc_existential_deposits: UserToPLMCBalance = contributors .iter() .map(|acc| (acc.clone(), get_ed())) .collect::>(); - let funding_asset_deposits = calculate_contributed_funding_asset_spent(contributions.clone(), ct_price); - let contributor_balances = sum_balance_mappings(vec![ - plmc_contribution_deposits.clone(), - plmc_existential_deposits.clone(), - ]); + let funding_asset_deposits = calculate_contributed_funding_asset_spent(contributions.clone(), ct_price); + let contributor_balances = + sum_balance_mappings(vec![necessary_plmc_mint.clone(), plmc_existential_deposits.clone()]); let expected_free_plmc_balances = merge_add_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); @@ -929,7 +933,7 @@ impl<'a> RemainderFundingProject<'a> { let prev_supply = test_env.get_plmc_total_supply(); let post_supply = prev_supply + contributor_balances; - test_env.mint_plmc_to(plmc_contribution_deposits.clone()); + test_env.mint_plmc_to(necessary_plmc_mint.clone()); test_env.mint_plmc_to(plmc_existential_deposits.clone()); test_env.mint_statemint_asset_to(funding_asset_deposits.clone()); @@ -1012,12 +1016,23 @@ impl<'a> FinishedProject<'a> { let prev_plmc_balances = test_env.get_free_plmc_balances_for(contributors.clone()); let prev_funding_asset_balances = test_env.get_free_statemint_asset_balances_for(asset_id, contributors.clone()); + let plmc_evaluation_deposits = calculate_evaluation_plmc_spent(evaluations.clone()); let plmc_bid_deposits = calculate_auction_plmc_spent(bids.clone()); - let plmc_community_contribution_deposits = calculate_contributed_plmc_spent(community_contributions.clone(), ct_price); - let plmc_remainder_contribution_deposits = calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); - let necessary_plmc_mint = merge_subtract_mappings_by_user(plmc_remainder_contribution_deposits.clone(), vec![plmc_evaluation_deposits]); - let total_plmc_participation_locked = merge_add_mappings_by_user(vec![plmc_bid_deposits, plmc_community_contribution_deposits, plmc_remainder_contribution_deposits.clone()]); + let plmc_community_contribution_deposits = + calculate_contributed_plmc_spent(community_contributions.clone(), ct_price); + let plmc_remainder_contribution_deposits = + calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); + + let necessary_plmc_mint = merge_subtract_mappings_by_user( + plmc_remainder_contribution_deposits.clone(), + vec![plmc_evaluation_deposits], + ); + let total_plmc_participation_locked = merge_add_mappings_by_user(vec![ + plmc_bid_deposits, + plmc_community_contribution_deposits, + plmc_remainder_contribution_deposits.clone(), + ]); let plmc_existential_deposits: UserToPLMCBalance = contributors .iter() .map(|acc| (acc.clone(), get_ed())) @@ -1025,11 +1040,8 @@ impl<'a> FinishedProject<'a> { let funding_asset_deposits = calculate_contributed_funding_asset_spent(remainder_contributions.clone(), ct_price); - - let contributor_balances = sum_balance_mappings(vec![ - necessary_plmc_mint.clone(), - plmc_existential_deposits.clone(), - ]); + let contributor_balances = + sum_balance_mappings(vec![necessary_plmc_mint.clone(), plmc_existential_deposits.clone()]); let expected_free_plmc_balances = merge_add_mappings_by_user(vec![prev_plmc_balances.clone(), plmc_existential_deposits.clone()]); @@ -1164,6 +1176,7 @@ mod defaults { } pub mod helper_functions { + use std::collections::BTreeMap; use super::*; pub fn get_ed() -> BalanceOf { @@ -1253,19 +1266,18 @@ pub mod helper_functions { let mut i = 0; let mut j = 0; while true { - let old_tup = old_output.get(i); + let old_tup = old_output.get(i); let new_tup = map.get(j); match (old_tup, new_tup) { (None, None) => break, (Some(_), None) => { output.extend_from_slice(&old_output[i..]); - break - }, + break; + } (None, Some(_)) => { output.extend_from_slice(&map[j..]); - break - + break; } (Some((acc_i, val_i)), Some((acc_j, val_j))) => { if acc_i == acc_j { @@ -1282,7 +1294,66 @@ pub mod helper_functions { } } } + } + output + } + + pub fn generic_map_merge_reduce( + mut mappings: Vec>, key_extractor: impl Fn(&M) -> K, initial_state: S, merge_reduce: impl Fn(&M, S) -> S, + ) -> Vec<(K, S)> { + let mut output = BTreeMap::new(); + for mut map in mappings { + for item in map.drain(..) { + let key = key_extractor(&item); + let new_state = merge_reduce(&item, output.get(&key).cloned().unwrap_or(initial_state.clone())); + output.insert(key, new_state); + } + } + output.into_iter().collect() + } + + pub fn generic_map_merge( + mut mappings: Vec>, key_extractor: impl Fn(&M) -> K, merger: impl Fn(&M, &M) -> M, + ) -> Vec { + let mut output = mappings.swap_remove(0); + output.sort_by_key(|k| key_extractor(k)); + for mut new_map in mappings { + new_map.sort_by_key(|k| key_extractor(k)); + let old_output = output.clone(); + output = Vec::new(); + let mut i = 0; + let mut j = 0; + loop { + let output_item = old_output.get(i); + let new_item = new_map.get(j); + match (output_item, new_item) { + (None, None) => break, + (Some(_), None) => { + output.extend_from_slice(&old_output[i..]); + break; + } + (None, Some(_)) => { + output.extend_from_slice(&new_map[j..]); + break; + } + (Some(m_i), Some(m_j)) => { + let k_i = key_extractor(m_i); + let k_j = key_extractor(m_j); + if k_i == k_j { + output.push(merger(m_i, m_j)); + i += 1; + j += 1; + } else if k_i < k_j { + output.push(old_output[i].clone()); + i += 1; + } else { + output.push(new_map[j].clone()); + j += 1; + } + } + } + } } output } @@ -1301,19 +1372,18 @@ pub mod helper_functions { let mut i = 0; let mut j = 0; while true { - let old_tup = old_output.get(i); + let old_tup = old_output.get(i); let new_tup = map.get(j); match (old_tup, new_tup) { (None, None) => break, (Some(_), None) => { output.extend_from_slice(&old_output[i..]); - break - }, + break; + } (None, Some(_)) => { output.extend_from_slice(&map[j..]); - break - + break; } (Some((acc_i, val_i)), Some((acc_j, val_j))) => { if acc_i == acc_j { @@ -1330,7 +1400,6 @@ pub mod helper_functions { } } } - } output } @@ -1671,11 +1740,16 @@ mod auction_round_success { let mut evaluations = default_evaluations(); let evaluator_bidder = 69; let evaluation_amount = 420 * US_DOLLAR; - let evaluator_bid = TestBid::new(evaluator_bidder, 600 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT); + let evaluator_bid = TestBid::new( + evaluator_bidder, + 600 * ASSET_UNIT, + 15.into(), + None, + AcceptedFundingAsset::USDT, + ); evaluations.push((evaluator_bidder, evaluation_amount)); - let bidding_project = - AuctioningProject::new_with(&test_env, project, issuer, evaluations); + let bidding_project = AuctioningProject::new_with(&test_env, project, issuer, evaluations); let already_bonded_plmc = calculate_evaluation_plmc_spent(vec![(evaluator_bidder, evaluation_amount)])[0].1; let necessary_plmc_for_bid = calculate_auction_plmc_spent(vec![evaluator_bid])[0].1; @@ -1694,11 +1768,23 @@ mod auction_round_success { let project = default_project(test_env.get_new_nonce()); let mut evaluations = default_evaluations(); let evaluator_bidder = 69; - let evaluator_bid = TestBid::new(evaluator_bidder, 600 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT); + let evaluator_bid = TestBid::new( + evaluator_bidder, + 600 * ASSET_UNIT, + 15.into(), + None, + AcceptedFundingAsset::USDT, + ); let mut bids = Vec::new(); for i in 0..::MaxBidsPerUser::get() { - bids.push(TestBid::new(evaluator_bidder, 10 * ASSET_UNIT, 15.into(), None, AcceptedFundingAsset::USDT)); + bids.push(TestBid::new( + evaluator_bidder, + 10 * ASSET_UNIT, + 15.into(), + None, + AcceptedFundingAsset::USDT, + )); } let mut fill_necessary_plmc_for_bids = calculate_auction_plmc_spent(bids.clone()); @@ -1711,11 +1797,12 @@ mod auction_round_success { const FUNDED_DELTA_PLMC: u128 = 69 * PLMC; evaluation_bond -= FUNDED_DELTA_PLMC; - let evaluation_usd_amount = ::PriceProvider::get_price(PLMC_STATEMINT_ID).unwrap().saturating_mul_int(evaluation_bond); + let evaluation_usd_amount = ::PriceProvider::get_price(PLMC_STATEMINT_ID) + .unwrap() + .saturating_mul_int(evaluation_bond); evaluations.push((evaluator_bidder, evaluation_usd_amount)); - let bidding_project = - AuctioningProject::new_with(&test_env, project, issuer, evaluations); + let bidding_project = AuctioningProject::new_with(&test_env, project, issuer, evaluations); let project_id = bidding_project.get_project_id(); test_env.mint_plmc_to(vec![(evaluator_bidder, FUNDED_DELTA_PLMC)]); @@ -1724,12 +1811,25 @@ mod auction_round_success { bidding_project.bid_for_users(bids).unwrap(); - let evaluation_bond = test_env.in_ext(|| ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &evaluator_bidder)); + let evaluation_bond = test_env.in_ext(|| { + ::NativeCurrency::balance_on_hold( + &LockType::Evaluation(project_id), + &evaluator_bidder, + ) + }); let post_fill_evaluation_bond = bid_necessary_plmc[0].1 - FUNDED_DELTA_PLMC; - assert!(evaluation_bond < post_fill_evaluation_bond + 10u128 || evaluation_bond > post_fill_evaluation_bond - 10u128); + assert!( + evaluation_bond < post_fill_evaluation_bond + 10u128 + || evaluation_bond > post_fill_evaluation_bond - 10u128 + ); bidding_project.bid_for_users(vec![evaluator_bid]).unwrap(); - let evaluation_bond = test_env.in_ext(|| ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &evaluator_bidder)); + let evaluation_bond = test_env.in_ext(|| { + ::NativeCurrency::balance_on_hold( + &LockType::Evaluation(project_id), + &evaluator_bidder, + ) + }); assert_eq!(evaluation_bond, 0); } @@ -1910,65 +2010,82 @@ mod auction_round_success { #[test] fn pallet_can_start_auction_automatically() { let test_env = TestEnvironment::new(); - let project = EvaluatingProject::new_with( - &test_env, - default_project(0), - ISSUER, - ); + let project = EvaluatingProject::new_with(&test_env, default_project(0), ISSUER); let evaluations = default_evaluations(); let required_plmc = calculate_evaluation_plmc_spent(evaluations.clone()); - let ed_plmc: UserToPLMCBalance = evaluations.clone().into_iter().map(|(account, amount)| (account, get_ed())).collect(); + let ed_plmc: UserToPLMCBalance = evaluations + .clone() + .into_iter() + .map(|(account, amount)| (account, get_ed())) + .collect(); test_env.mint_plmc_to(required_plmc); test_env.mint_plmc_to(ed_plmc); project.bond_for_users(evaluations).unwrap(); test_env.advance_time(::EvaluationDuration::get() + 1); - assert_eq!(project.get_project_details().status, ProjectStatus::AuctionInitializePeriod); + assert_eq!( + project.get_project_details().status, + ProjectStatus::AuctionInitializePeriod + ); test_env.advance_time(::AuctionInitializePeriodDuration::get() + 2); - assert_eq!(project.get_project_details().status, ProjectStatus::AuctionRound(AuctionPhase::English)); + assert_eq!( + project.get_project_details().status, + ProjectStatus::AuctionRound(AuctionPhase::English) + ); } #[test] fn issuer_can_start_auction_manually() { let test_env = TestEnvironment::new(); - let project = EvaluatingProject::new_with( - &test_env, - default_project(0), - ISSUER, - ); + let project = EvaluatingProject::new_with(&test_env, default_project(0), ISSUER); let evaluations = default_evaluations(); let required_plmc = calculate_evaluation_plmc_spent(evaluations.clone()); - let ed_plmc: UserToPLMCBalance = evaluations.clone().into_iter().map(|(account, amount)| (account, get_ed())).collect(); + let ed_plmc: UserToPLMCBalance = evaluations + .clone() + .into_iter() + .map(|(account, amount)| (account, get_ed())) + .collect(); test_env.mint_plmc_to(required_plmc); test_env.mint_plmc_to(ed_plmc); project.bond_for_users(evaluations).unwrap(); test_env.advance_time(::EvaluationDuration::get() + 1); - assert_eq!(project.get_project_details().status, ProjectStatus::AuctionInitializePeriod); + assert_eq!( + project.get_project_details().status, + ProjectStatus::AuctionInitializePeriod + ); test_env.advance_time(1); - test_env.in_ext(|| FundingModule::start_auction(RuntimeOrigin::signed(ISSUER), project.get_project_id())).unwrap(); - assert_eq!(project.get_project_details().status, ProjectStatus::AuctionRound(AuctionPhase::English)); + test_env + .in_ext(|| FundingModule::start_auction(RuntimeOrigin::signed(ISSUER), project.get_project_id())) + .unwrap(); + assert_eq!( + project.get_project_details().status, + ProjectStatus::AuctionRound(AuctionPhase::English) + ); } #[test] fn stranger_cannot_start_auction_manually() { let test_env = TestEnvironment::new(); - let project = EvaluatingProject::new_with( - &test_env, - default_project(0), - ISSUER, - ); + let project = EvaluatingProject::new_with(&test_env, default_project(0), ISSUER); let evaluations = default_evaluations(); let required_plmc = calculate_evaluation_plmc_spent(evaluations.clone()); - let ed_plmc: UserToPLMCBalance = evaluations.clone().into_iter().map(|(account, amount)| (account, get_ed())).collect(); + let ed_plmc: UserToPLMCBalance = evaluations + .clone() + .into_iter() + .map(|(account, amount)| (account, get_ed())) + .collect(); test_env.mint_plmc_to(required_plmc); test_env.mint_plmc_to(ed_plmc); project.bond_for_users(evaluations).unwrap(); test_env.advance_time(::EvaluationDuration::get() + 1); - assert_eq!(project.get_project_details().status, ProjectStatus::AuctionInitializePeriod); + assert_eq!( + project.get_project_details().status, + ProjectStatus::AuctionInitializePeriod + ); test_env.advance_time(1); for account in 6000..6010 { - test_env.in_ext(|| { + test_env.in_ext(|| { let response = FundingModule::start_auction(RuntimeOrigin::signed(account), project.get_project_id()); assert_noop!(response, Error::::NotAllowed); }); @@ -1983,9 +2100,13 @@ mod auction_round_success { let evaluations = default_evaluations(); let mut bids = default_bids(); let evaluator = evaluations[0].0; - bids.push( - TestBid::new(evaluator, 150 * ASSET_UNIT, 21u128.into(), None, AcceptedFundingAsset::USDT) - ); + bids.push(TestBid::new( + evaluator, + 150 * ASSET_UNIT, + 21u128.into(), + None, + AcceptedFundingAsset::USDT, + )); let _community_funding_project = CommunityFundingProject::new_with(&test_env, project, issuer, evaluations, bids); } @@ -2586,57 +2707,82 @@ mod community_round_success { } #[test] - fn evaluation_bond_counts_towards_community_contribution() { + fn retail_contributor_was_evaluator() { let test_env = TestEnvironment::new(); let issuer = ISSUER; let project = default_project(test_env.get_new_nonce()); let mut evaluations = default_evaluations(); let evaluator_contributor = 69; let evaluation_amount = 420 * US_DOLLAR; - let contribution = TestContribution::new(evaluator_contributor, 600 * ASSET_UNIT, None, AcceptedFundingAsset::USDT); + let contribution = TestContribution::new( + evaluator_contributor, + 600 * ASSET_UNIT, + None, + AcceptedFundingAsset::USDT, + ); evaluations.push((evaluator_contributor, evaluation_amount)); let bids = default_bids(); - let contributing_project = - CommunityFundingProject::new_with(&test_env, project, issuer, evaluations, bids); - let ct_price = contributing_project.get_project_details().weighted_average_price.unwrap(); - let already_bonded_plmc = calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount)])[0].1; + let contributing_project = CommunityFundingProject::new_with(&test_env, project, issuer, evaluations, bids); + let ct_price = contributing_project + .get_project_details() + .weighted_average_price + .unwrap(); + let already_bonded_plmc = + calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount)])[0].1; let necessary_plmc_for_bid = calculate_contributed_plmc_spent(vec![contribution], ct_price)[0].1; let necessary_usdt_for_bid = calculate_contributed_funding_asset_spent(vec![contribution], ct_price); - test_env.mint_plmc_to(vec![(evaluator_contributor, necessary_plmc_for_bid - already_bonded_plmc)]); + test_env.mint_plmc_to(vec![( + evaluator_contributor, + necessary_plmc_for_bid - already_bonded_plmc, + )]); test_env.mint_statemint_asset_to(necessary_usdt_for_bid); contributing_project.buy_for_retail_users(vec![contribution]).unwrap(); } #[test] - fn evaluation_bond_counts_towards_community_contribution_vec_full() { + fn retail_contributor_was_evaluator_vec_full() { let test_env = TestEnvironment::new(); let issuer = ISSUER; let project = default_project(test_env.get_new_nonce()); let mut evaluations = default_evaluations(); let bids = default_bids(); let evaluator_contributor = 69; - let overflow_contribution = TestContribution::new(evaluator_contributor, 600 * ASSET_UNIT, None, AcceptedFundingAsset::USDT); + let overflow_contribution = TestContribution::new( + evaluator_contributor, + 600 * ASSET_UNIT, + None, + AcceptedFundingAsset::USDT, + ); let mut fill_contributions = Vec::new(); for i in 0..::MaxContributionsPerUser::get() { - fill_contributions.push(TestContribution::new(evaluator_contributor, 10 * ASSET_UNIT, None, AcceptedFundingAsset::USDT)); + fill_contributions.push(TestContribution::new( + evaluator_contributor, + 10 * ASSET_UNIT, + None, + AcceptedFundingAsset::USDT, + )); } let expected_price = FixedU128::from_float(38.3333333333f64); let mut fill_necessary_plmc = calculate_contributed_plmc_spent(fill_contributions.clone(), expected_price); - let fill_necessary_usdt_for_bids = calculate_contributed_funding_asset_spent(fill_contributions.clone(), expected_price); + let fill_necessary_usdt_for_bids = + calculate_contributed_funding_asset_spent(fill_contributions.clone(), expected_price); let overflow_necessary_plmc = calculate_contributed_plmc_spent(vec![overflow_contribution], expected_price); - let overflow_necessary_usdt = calculate_contributed_funding_asset_spent(vec![overflow_contribution], expected_price); + let overflow_necessary_usdt = + calculate_contributed_funding_asset_spent(vec![overflow_contribution], expected_price); let mut evaluation_bond = sum_balance_mappings(vec![fill_necessary_plmc, overflow_necessary_plmc.clone()]); const FUNDED_DELTA_PLMC: u128 = 69 * PLMC; evaluation_bond -= FUNDED_DELTA_PLMC; - let evaluation_usd_amount = ::PriceProvider::get_price(PLMC_STATEMINT_ID).unwrap().saturating_mul_int(evaluation_bond); + let evaluation_usd_amount = ::PriceProvider::get_price(PLMC_STATEMINT_ID) + .unwrap() + .saturating_mul_int(evaluation_bond); evaluations.push((evaluator_contributor, evaluation_usd_amount)); let community_funding_project = @@ -2647,14 +2793,31 @@ mod community_round_success { test_env.mint_statemint_asset_to(fill_necessary_usdt_for_bids); test_env.mint_statemint_asset_to(overflow_necessary_usdt); - community_funding_project.buy_for_retail_users(fill_contributions).unwrap(); + community_funding_project + .buy_for_retail_users(fill_contributions) + .unwrap(); - let evaluation_bond = test_env.in_ext(|| ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &evaluator_contributor)); + let evaluation_bond = test_env.in_ext(|| { + ::NativeCurrency::balance_on_hold( + &LockType::Evaluation(project_id), + &evaluator_contributor, + ) + }); let post_fill_evaluation_bond = overflow_necessary_plmc[0].1 - FUNDED_DELTA_PLMC; - assert!(evaluation_bond < post_fill_evaluation_bond + 10u128 || evaluation_bond > post_fill_evaluation_bond - 10u128); + assert!( + evaluation_bond < post_fill_evaluation_bond + 10u128 + || evaluation_bond > post_fill_evaluation_bond - 10u128 + ); - community_funding_project.buy_for_retail_users(vec![overflow_contribution]).unwrap(); - let evaluation_bond = test_env.in_ext(|| ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &evaluator_contributor)); + community_funding_project + .buy_for_retail_users(vec![overflow_contribution]) + .unwrap(); + let evaluation_bond = test_env.in_ext(|| { + ::NativeCurrency::balance_on_hold( + &LockType::Evaluation(project_id), + &evaluator_contributor, + ) + }); assert_eq!(evaluation_bond, 0); } } @@ -2681,6 +2844,123 @@ mod remainder_round_success { default_remainder_buys(), ); } + + #[test] + fn remainder_contributor_was_evaluator() { + let test_env = TestEnvironment::new(); + let issuer = ISSUER; + let project = default_project(test_env.get_new_nonce()); + let mut evaluations = default_evaluations(); + let community_contributions = default_community_buys(); + let evaluator_contributor = 69; + let evaluation_amount = 420 * US_DOLLAR; + let remainder_contribution = TestContribution::new( + evaluator_contributor, + 600 * ASSET_UNIT, + None, + AcceptedFundingAsset::USDT, + ); + evaluations.push((evaluator_contributor, evaluation_amount)); + let bids = default_bids(); + + let contributing_project = + RemainderFundingProject::new_with(&test_env, project, issuer, evaluations, bids, community_contributions); + let ct_price = contributing_project + .get_project_details() + .weighted_average_price + .unwrap(); + let already_bonded_plmc = + calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount)])[0].1; + let necessary_plmc_for_buy = calculate_contributed_plmc_spent(vec![remainder_contribution], ct_price)[0].1; + let necessary_usdt_for_buy = calculate_contributed_funding_asset_spent(vec![remainder_contribution], ct_price); + + test_env.mint_plmc_to(vec![( + evaluator_contributor, + necessary_plmc_for_buy - already_bonded_plmc, + )]); + test_env.mint_statemint_asset_to(necessary_usdt_for_buy); + + contributing_project + .buy_for_any_user(vec![remainder_contribution]) + .unwrap(); + } + + #[test] + fn remainder_contributor_was_evaluator_vec_full() { + let test_env = TestEnvironment::new(); + let issuer = ISSUER; + let project = default_project(test_env.get_new_nonce()); + let mut evaluations = default_evaluations(); + let bids = default_bids(); + let evaluator_contributor = 69; + let overflow_contribution = TestContribution::new( + evaluator_contributor, + 600 * ASSET_UNIT, + None, + AcceptedFundingAsset::USDT, + ); + + let mut fill_contributions = Vec::new(); + for i in 0..::MaxContributionsPerUser::get() { + fill_contributions.push(TestContribution::new( + evaluator_contributor, + 10 * ASSET_UNIT, + None, + AcceptedFundingAsset::USDT, + )); + } + + let expected_price = FixedU128::from_float(38.3333333333f64); + let mut fill_necessary_plmc = calculate_contributed_plmc_spent(fill_contributions.clone(), expected_price); + let fill_necessary_usdt_for_bids = + calculate_contributed_funding_asset_spent(fill_contributions.clone(), expected_price); + + let overflow_necessary_plmc = calculate_contributed_plmc_spent(vec![overflow_contribution], expected_price); + let overflow_necessary_usdt = + calculate_contributed_funding_asset_spent(vec![overflow_contribution], expected_price); + + let mut evaluation_bond = sum_balance_mappings(vec![fill_necessary_plmc, overflow_necessary_plmc.clone()]); + const FUNDED_DELTA_PLMC: u128 = 69 * PLMC; + evaluation_bond -= FUNDED_DELTA_PLMC; + + let evaluation_usd_amount = ::PriceProvider::get_price(PLMC_STATEMINT_ID) + .unwrap() + .saturating_mul_int(evaluation_bond); + evaluations.push((evaluator_contributor, evaluation_usd_amount)); + + let remainder_funding_project = + RemainderFundingProject::new_with(&test_env, project, issuer, evaluations, bids, default_community_buys()); + let project_id = remainder_funding_project.get_project_id(); + + test_env.mint_plmc_to(vec![(evaluator_contributor, FUNDED_DELTA_PLMC)]); + test_env.mint_statemint_asset_to(fill_necessary_usdt_for_bids); + test_env.mint_statemint_asset_to(overflow_necessary_usdt); + + remainder_funding_project.buy_for_any_user(fill_contributions).unwrap(); + + let evaluation_bond = test_env.in_ext(|| { + ::NativeCurrency::balance_on_hold( + &LockType::Evaluation(project_id), + &evaluator_contributor, + ) + }); + let post_fill_evaluation_bond = overflow_necessary_plmc[0].1 - FUNDED_DELTA_PLMC; + assert!( + evaluation_bond < post_fill_evaluation_bond + 10u128 + || evaluation_bond > post_fill_evaluation_bond - 10u128 + ); + + remainder_funding_project + .buy_for_any_user(vec![overflow_contribution]) + .unwrap(); + let evaluation_bond = test_env.in_ext(|| { + ::NativeCurrency::balance_on_hold( + &LockType::Evaluation(project_id), + &evaluator_contributor, + ) + }); + assert_eq!(evaluation_bond, 0); + } } #[cfg(test)] @@ -2688,10 +2968,11 @@ mod purchased_vesting { use super::*; #[test] - fn contribution_token_mints() { + fn individual_contribution_token_mints() { // TODO: currently the vesting is limited to the whole payment at once. We should test it with several payments over a vesting period. let test_env = TestEnvironment::new(); let community_contributions = default_community_buys(); + let remainder_contributions = default_remainder_buys(); let finished_project = FinishedProject::new_with( &test_env, default_project(test_env.get_new_nonce()), @@ -2699,23 +2980,33 @@ mod purchased_vesting { default_evaluations(), default_bids(), community_contributions.clone(), - default_remainder_buys(), + remainder_contributions.clone(), ); let project_id = finished_project.project_id; + let user_buys = generic_map_merge( + vec![community_contributions.clone(), default_remainder_buys()], + |m| m.contributor.clone(), + |mut m1, m2| { + let total_amount = m1.amount.clone() + m2.amount.clone(); + let mut mx = m1.clone(); + mx.amount = total_amount; + mx + }, + ); - for contribution in community_contributions { + for merged_contribution in user_buys { let result = test_env.in_ext(|| { FundingModule::vested_contribution_token_purchase_mint_for( - RuntimeOrigin::signed(contribution.contributor), + RuntimeOrigin::signed(merged_contribution.contributor), project_id, - contribution.contributor, + merged_contribution.contributor, ) }); assert_ok!(result); let minted_balance = test_env.in_ext(|| { - ::ContributionTokenCurrency::balance(project_id, contribution.contributor) + ::ContributionTokenCurrency::balance(project_id, merged_contribution.contributor) }); - let desired_balance = contribution.amount; + let desired_balance = merged_contribution.amount; assert_eq!(minted_balance, desired_balance); } } @@ -2723,47 +3014,63 @@ mod purchased_vesting { #[test] fn plmc_unbonded() { let test_env = TestEnvironment::new(); + let evaluations = default_evaluations(); + let bids = default_bids(); let community_contributions = default_community_buys(); + let remainder_contributions = default_remainder_buys(); let finished_project = FinishedProject::new_with( &test_env, default_project(test_env.get_new_nonce()), ISSUER, - default_evaluations(), - default_bids(), + evaluations.clone(), + bids.clone(), community_contributions.clone(), - default_remainder_buys(), + remainder_contributions.clone(), ); let project_id = finished_project.project_id; let token_price = finished_project.get_project_details().weighted_average_price.unwrap(); - for contribution in community_contributions { + let bidders_plmc_bond = calculate_auction_plmc_spent(bids.clone()); + let contributors_plmc_spent: UserToPLMCBalance = generic_map_merge_reduce( + vec![community_contributions.clone(), remainder_contributions.clone()], + |m| m.contributor.clone(), + 0u128, + |contribution, total_plmc_spent| { + let new_plmc = calculate_contributed_plmc_spent(vec![contribution.clone()], token_price)[0].1; + total_plmc_spent.checked_add(new_plmc).unwrap() + }, + ); + + let participation_locked_plmc = merge_add_mappings_by_user(vec![bidders_plmc_bond.clone(), contributors_plmc_spent.clone()]); + let purchase_unbonds = merge_subtract_mappings_by_user(participation_locked_plmc.clone(), vec![bidders_plmc_bond.clone()]); + + + + for ((user, pre_locked), (_, post_released)) in zip(participation_locked_plmc, purchase_unbonds) { let actual_bonded_plmc = test_env.in_ext(|| { ::NativeCurrency::balance_on_hold( &LockType::Participation(project_id), - &contribution.contributor, + &user, ) }); - let desired_bonded_plmc = sum_balance_mappings(vec![calculate_contributed_plmc_spent( - vec![contribution.clone()], - token_price, - )]); - assert_eq!(actual_bonded_plmc, desired_bonded_plmc); + + assert_eq!(actual_bonded_plmc, pre_locked); let result = test_env.in_ext(|| { FundingModule::vested_plmc_purchase_unbond_for( - RuntimeOrigin::signed(contribution.contributor), + RuntimeOrigin::signed(user), project_id, - contribution.contributor, + user, ) }); let actual_bonded_plmc = test_env.in_ext(|| { ::NativeCurrency::balance_on_hold( &LockType::Participation(project_id), - &contribution.contributor, + &user, ) }); assert_ok!(result); - assert_eq!(actual_bonded_plmc, 0u32.into()); + assert_eq!(actual_bonded_plmc, pre_locked - post_released); } } } @@ -2825,20 +3132,37 @@ mod bids_vesting { let ct_price = finished_project.get_project_details().weighted_average_price.unwrap(); let plmc_bid_deposits = calculate_auction_plmc_spent(bids.clone()); - let plmc_community_contribution_deposits = calculate_contributed_plmc_spent(community_contributions.clone(), ct_price); - let plmc_remainder_contribution_deposits = calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); - let total_plmc_participation_locked = merge_add_mappings_by_user(vec![plmc_bid_deposits.clone(), plmc_community_contribution_deposits, plmc_remainder_contribution_deposits.clone()]); + let plmc_community_contribution_deposits = + calculate_contributed_plmc_spent(community_contributions.clone(), ct_price); + let plmc_remainder_contribution_deposits = + calculate_contributed_plmc_spent(remainder_contributions.clone(), ct_price); + let total_plmc_participation_locked = merge_add_mappings_by_user(vec![ + plmc_bid_deposits.clone(), + plmc_community_contribution_deposits, + plmc_remainder_contribution_deposits.clone(), + ]); - test_env.do_reserved_plmc_assertions(total_plmc_participation_locked.clone(), LockType::Participation(project_id)); + test_env.do_reserved_plmc_assertions( + total_plmc_participation_locked.clone(), + LockType::Participation(project_id), + ); for (bidder, deposit) in plmc_bid_deposits { - let bidder_participation_locked = total_plmc_participation_locked.clone().into_iter().find(|(acc, _)| acc.clone() == bidder.clone()).unwrap().1; + let bidder_participation_locked = total_plmc_participation_locked + .clone() + .into_iter() + .find(|(acc, _)| acc.clone() == bidder.clone()) + .unwrap() + .1; let result = test_env.in_ext(|| { FundingModule::vested_plmc_bid_unbond_for(RuntimeOrigin::signed(bidder.clone()), project_id, bidder) }); assert_ok!(result); - test_env.do_reserved_plmc_assertions(vec![(bidder, bidder_participation_locked - deposit)], LockType::Participation(project_id)); + test_env.do_reserved_plmc_assertions( + vec![(bidder, bidder_participation_locked - deposit)], + LockType::Participation(project_id), + ); } } } From 0afa01a6f8591ef9f18cf26acbf4f567880f18a3 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Fri, 23 Jun 2023 17:21:33 +0200 Subject: [PATCH 6/9] feat(209): new tests for the test helper functions used to calculate PLMC amount required on evaluations, bids, and contributions --- polimec-skeleton/pallets/funding/src/tests.rs | 254 +++++++++++++++--- 1 file changed, 218 insertions(+), 36 deletions(-) diff --git a/polimec-skeleton/pallets/funding/src/tests.rs b/polimec-skeleton/pallets/funding/src/tests.rs index 61fd0900d..866b190fe 100644 --- a/polimec-skeleton/pallets/funding/src/tests.rs +++ b/polimec-skeleton/pallets/funding/src/tests.rs @@ -197,7 +197,7 @@ const BUYER_1: AccountId = 7; const BUYER_2: AccountId = 8; const ASSET_DECIMALS: u8 = 10; -const ASSET_UNIT: u128 = 10u128.pow(ASSET_DECIMALS as u32); +const ASSET_UNIT: u128 = 10_u128.pow(ASSET_DECIMALS as u32); const USDT_STATEMINT_ID: AssetId = 1984u32; const USDT_UNIT: u128 = 10_000_000_000_u128; @@ -1145,14 +1145,14 @@ mod defaults { TestBid::new( BIDDER_1, 3000 * ASSET_UNIT, - 50u128.into(), + 50_u128.into(), None, AcceptedFundingAsset::USDT, ), TestBid::new( BIDDER_2, 5000 * ASSET_UNIT, - 15u128.into(), + 15_u128.into(), None, AcceptedFundingAsset::USDT, ), @@ -1504,7 +1504,7 @@ mod creation_round_failure { #[test] fn price_too_low() { let wrong_project: ProjectMetadataOf = ProjectMetadata { - minimum_price: 0u128.into(), + minimum_price: 0_u128.into(), ticket_size: TicketSize { minimum: Some(1), maximum: None, @@ -1526,7 +1526,7 @@ mod creation_round_failure { #[test] fn participants_size_error() { let wrong_project: ProjectMetadataOf = ProjectMetadata { - minimum_price: 1u128.into(), + minimum_price: 1_u128.into(), ticket_size: TicketSize { minimum: Some(1), maximum: None, @@ -1549,7 +1549,7 @@ mod creation_round_failure { #[test] fn ticket_size_error() { let wrong_project: ProjectMetadataOf = ProjectMetadata { - minimum_price: 1u128.into(), + minimum_price: 1_u128.into(), ticket_size: TicketSize { minimum: None, maximum: None, @@ -1573,7 +1573,7 @@ mod creation_round_failure { #[ignore = "ATM only the first error will be thrown"] fn multiple_field_error() { let wrong_project: ProjectMetadataOf = ProjectMetadata { - minimum_price: 0u128.into(), + minimum_price: 0_u128.into(), ticket_size: TicketSize { minimum: None, maximum: None, @@ -1819,8 +1819,8 @@ mod auction_round_success { }); let post_fill_evaluation_bond = bid_necessary_plmc[0].1 - FUNDED_DELTA_PLMC; assert!( - evaluation_bond < post_fill_evaluation_bond + 10u128 - || evaluation_bond > post_fill_evaluation_bond - 10u128 + evaluation_bond < post_fill_evaluation_bond + 10_u128 + || evaluation_bond > post_fill_evaluation_bond - 10_u128 ); bidding_project.bid_for_users(vec![evaluator_bid]).unwrap(); @@ -1882,10 +1882,10 @@ mod auction_round_success { .weighted_average_price .unwrap(); - let price_in_10_decimals = token_price.checked_mul_int(1_0_000_000_000u128).unwrap(); - let price_in_12_decimals = token_price.checked_mul_int(1_000_000_000_000u128).unwrap(); - assert_eq!(price_in_10_decimals, 16_3_333_333_333u128); - assert_eq!(price_in_12_decimals, 16_333_333_333_333u128); + let price_in_10_decimals = token_price.checked_mul_int(1_0_000_000_000_u128).unwrap(); + let price_in_12_decimals = token_price.checked_mul_int(1_000_000_000_000_u128).unwrap(); + assert_eq!(price_in_10_decimals, 16_3_333_333_333_u128); + assert_eq!(price_in_12_decimals, 16_333_333_333_333_u128); } #[test] @@ -2103,7 +2103,7 @@ mod auction_round_success { bids.push(TestBid::new( evaluator, 150 * ASSET_UNIT, - 21u128.into(), + 21_u128.into(), None, AcceptedFundingAsset::USDT, )); @@ -2140,7 +2140,7 @@ mod auction_round_failure { RuntimeOrigin::signed(BIDDER_2), 0, 1, - 100u128.into(), + 100_u128.into(), None, AcceptedFundingAsset::USDT ), @@ -2176,11 +2176,11 @@ mod auction_round_failure { let project_id = auctioning_project.project_id; const DAVE: AccountId = 42; let bids: TestBids = vec![ - TestBid::new(DAVE, 10_000 * USDT_UNIT, 2u128.into(), None, AcceptedFundingAsset::USDT), // 20k - TestBid::new(DAVE, 12_000 * USDT_UNIT, 8u128.into(), None, AcceptedFundingAsset::USDT), // 96k - TestBid::new(DAVE, 15_000 * USDT_UNIT, 5u128.into(), None, AcceptedFundingAsset::USDT), // 75k - TestBid::new(DAVE, 1_000 * USDT_UNIT, 7u128.into(), None, AcceptedFundingAsset::USDT), // 7k - TestBid::new(DAVE, 20_000 * USDT_UNIT, 5u128.into(), None, AcceptedFundingAsset::USDT), // 100k + TestBid::new(DAVE, 10_000 * USDT_UNIT, 2_u128.into(), None, AcceptedFundingAsset::USDT), // 20k + TestBid::new(DAVE, 12_000 * USDT_UNIT, 8_u128.into(), None, AcceptedFundingAsset::USDT), // 96k + TestBid::new(DAVE, 15_000 * USDT_UNIT, 5_u128.into(), None, AcceptedFundingAsset::USDT), // 75k + TestBid::new(DAVE, 1_000 * USDT_UNIT, 7_u128.into(), None, AcceptedFundingAsset::USDT), // 7k + TestBid::new(DAVE, 20_000 * USDT_UNIT, 5_u128.into(), None, AcceptedFundingAsset::USDT), // 100k ]; let mut plmc_fundings: UserToPLMCBalance = calculate_auction_plmc_spent(bids.clone()); @@ -2200,10 +2200,10 @@ mod auction_round_failure { test_env.ext_env.borrow_mut().execute_with(|| { let stored_bids = FundingModule::bids(project_id, DAVE); assert_eq!(stored_bids.len(), 4); - assert_eq!(stored_bids[0].ct_usd_price, 5u128.into()); - assert_eq!(stored_bids[1].ct_usd_price, 8u128.into()); - assert_eq!(stored_bids[2].ct_usd_price, 5u128.into()); - assert_eq!(stored_bids[3].ct_usd_price, 2u128.into()); + assert_eq!(stored_bids[0].ct_usd_price, 5_u128.into()); + assert_eq!(stored_bids[1].ct_usd_price, 8_u128.into()); + assert_eq!(stored_bids[2].ct_usd_price, 5_u128.into()); + assert_eq!(stored_bids[3].ct_usd_price, 2_u128.into()); }); } @@ -2214,8 +2214,8 @@ mod auction_round_failure { AuctioningProject::new_with(&test_env, default_project(0), ISSUER, default_evaluations()); let mul_2 = MultiplierOf::::from(2u32); let bids = vec![ - TestBid::new(BIDDER_1, 10_000, 2u128.into(), None, AcceptedFundingAsset::USDC), - TestBid::new(BIDDER_2, 13_000, 3u128.into(), Some(mul_2), AcceptedFundingAsset::USDC), + TestBid::new(BIDDER_1, 10_000, 2_u128.into(), None, AcceptedFundingAsset::USDC), + TestBid::new(BIDDER_2, 13_000, 3_u128.into(), Some(mul_2), AcceptedFundingAsset::USDC), ]; let outcome = auctioning_project.bid_for_users(bids); frame_support::assert_err!(outcome, Error::::FundingAssetNotAccepted); @@ -2390,7 +2390,7 @@ mod community_round_success { ); test_env.do_free_plmc_assertions(vec![plmc_fundings[1].clone()]); - test_env.do_free_statemint_asset_assertions(vec![(BOB, 0u128, AcceptedFundingAsset::USDT.to_statemint_id())]); + test_env.do_free_statemint_asset_assertions(vec![(BOB, 0_u128, AcceptedFundingAsset::USDT.to_statemint_id())]); test_env.do_reserved_plmc_assertions(vec![plmc_fundings[0].clone()], LockType::Participation(project_id)); test_env.do_contribution_transferred_statemint_asset_assertions( statemint_asset_fundings, @@ -2456,12 +2456,12 @@ mod community_round_success { ); let reserved_plmc = plmc_fundings.swap_remove(0).1; - let remaining_plmc: Balance = plmc_fundings.iter().fold(0u128, |acc, (_, amount)| acc + amount); + let remaining_plmc: Balance = plmc_fundings.iter().fold(0_u128, |acc, (_, amount)| acc + amount); let actual_funding_transferred = statemint_asset_fundings.swap_remove(0).1; let remaining_statemint_assets: Balance = statemint_asset_fundings .iter() - .fold(0u128, |acc, (_, amount, _)| acc + amount); + .fold(0_u128, |acc, (_, amount, _)| acc + amount); test_env.do_free_plmc_assertions(vec![(BOB, remaining_plmc)]); test_env.do_free_statemint_asset_assertions(vec![( @@ -2805,8 +2805,8 @@ mod community_round_success { }); let post_fill_evaluation_bond = overflow_necessary_plmc[0].1 - FUNDED_DELTA_PLMC; assert!( - evaluation_bond < post_fill_evaluation_bond + 10u128 - || evaluation_bond > post_fill_evaluation_bond - 10u128 + evaluation_bond < post_fill_evaluation_bond + 10_u128 + || evaluation_bond > post_fill_evaluation_bond - 10_u128 ); community_funding_project @@ -2946,8 +2946,8 @@ mod remainder_round_success { }); let post_fill_evaluation_bond = overflow_necessary_plmc[0].1 - FUNDED_DELTA_PLMC; assert!( - evaluation_bond < post_fill_evaluation_bond + 10u128 - || evaluation_bond > post_fill_evaluation_bond - 10u128 + evaluation_bond < post_fill_evaluation_bond + 10_u128 + || evaluation_bond > post_fill_evaluation_bond - 10_u128 ); remainder_funding_project @@ -3034,7 +3034,7 @@ mod purchased_vesting { let contributors_plmc_spent: UserToPLMCBalance = generic_map_merge_reduce( vec![community_contributions.clone(), remainder_contributions.clone()], |m| m.contributor.clone(), - 0u128, + 0_u128, |contribution, total_plmc_spent| { let new_plmc = calculate_contributed_plmc_spent(vec![contribution.clone()], token_price)[0].1; total_plmc_spent.checked_add(new_plmc).unwrap() @@ -3167,6 +3167,188 @@ mod bids_vesting { } } +#[cfg(test)] +mod test_helper_functions { + use super::*; + + #[test] + fn calculate_evaluation_plmc_spent() { + const EVALUATOR_1: AccountIdOf = 1u64; + const USD_AMOUNT_1: u128 = 150_000_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_1: u128 = 17_857_1_428_571_428_u128; + + const EVALUATOR_2: AccountIdOf = 2u64; + const USD_AMOUNT_2: u128 = 50_000_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_2: u128 = 5_952_3_809_523_809_u128; + + const EVALUATOR_3: AccountIdOf = 3u64; + const USD_AMOUNT_3: u128 = 75_000_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_3: u128 = 8_928_5_714_285_714_u128; + + const EVALUATOR_4: AccountIdOf = 4u64; + const USD_AMOUNT_4: u128 = 100_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_4: u128 = 11_9_047_619_047_u128; + + const EVALUATOR_5: AccountIdOf = 5u64; + const USD_AMOUNT_5: u128 = 123_7_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_5: u128 = 14_7_261_904_761_u128; + + const PLMC_PRICE: f64 = 8.4f64; + + assert_eq!( + ::PriceProvider::get_price(PLMC_STATEMINT_ID.into()).unwrap(), + PriceOf::::from_float(PLMC_PRICE) + ); + + let evaluations = vec![ + (EVALUATOR_1, USD_AMOUNT_1), + (EVALUATOR_2, USD_AMOUNT_2), + (EVALUATOR_3, USD_AMOUNT_3), + (EVALUATOR_4, USD_AMOUNT_4), + (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 result = super::helper_functions::calculate_evaluation_plmc_spent(evaluations); + assert_eq!(result, expected_plmc_spent); + } + + #[test] + fn calculate_auction_plmc_spent() { + const BIDDER_1: AccountIdOf = 1u64; + const TOKEN_AMOUNT_1: u128 = 120_0_000_000_000_u128; + const PRICE_PER_TOKEN_1: f64 = 0.3f64; + const MULTIPLIER_1: u32 = 1u32; + const TICKET_SIZE_USD_1: u128 = 36_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_1: u128 = 4_2_857_142_857_u128; + + const BIDDER_2: AccountIdOf = 2u64; + const TOKEN_AMOUNT_2: u128 = 5023_0_000_000_000_u128; + const PRICE_PER_TOKEN_2: f64 = 13f64; + const MULTIPLIER_2: u32 = 2u32; + const TICKET_SIZE_USD_2: u128 = 65_299_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_2: u128 = 3_886_8_452_380_952_u128; + + const BIDDER_3: AccountIdOf = 3u64; + const TOKEN_AMOUNT_3: u128 = 20_000_0_000_000_000_u128; + const PRICE_PER_TOKEN_3: f64 = 20f64; + const MULTIPLIER_3: u32 = 17u32; + const TICKET_SIZE_USD_3: u128 = 400_000_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_3: u128 = 2_801_1_204_481_792_u128; + + const BIDDER_4: AccountIdOf = 4u64; + const TOKEN_AMOUNT_4: u128 = 1_000_000_0_000_000_000_u128; + const PRICE_PER_TOKEN_4: f64 = 5.52f64; + const MULTIPLIER_4: u32 = 25u32; + const TICKET_SIZE_4: u128 = 5_520_000_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_4: u128 = 26_285_7_142_857_142_u128; + + const BIDDER_5: AccountIdOf = 5u64; + const TOKEN_AMOUNT_5: u128 = 0_1_233_000_000_u128; + const PRICE_PER_TOKEN_5: f64 = 11.34f64; + const MULTIPLIER_5: u32 = 10u32; + const TICKET_SIZE_5: u128 = 1_3_982_220_000_u128; + // TODO: Is this due to rounding errors? + // Should be in reality 0.0166455, but we get 0.0166454999. i.e error of 0.0000000001 PLMC + const EXPECTED_PLMC_AMOUNT_5: u128 = 0_0_166_454_999_u128; + + const PLMC_PRICE: f64 = 8.4f64; + + assert_eq!( + ::PriceProvider::get_price(PLMC_STATEMINT_ID.into()).unwrap(), + PriceOf::::from_float(PLMC_PRICE) + ); + + let bids = vec![ + TestBid::new(BIDDER_1, TOKEN_AMOUNT_1, PriceOf::::from_float(PRICE_PER_TOKEN_1), Some(MultiplierOf::::from(MULTIPLIER_1)), AcceptedFundingAsset::USDT), + TestBid::new(BIDDER_2, TOKEN_AMOUNT_2, PriceOf::::from_float(PRICE_PER_TOKEN_2), Some(MultiplierOf::::from(MULTIPLIER_2)), AcceptedFundingAsset::USDT), + TestBid::new(BIDDER_3, TOKEN_AMOUNT_3, PriceOf::::from_float(PRICE_PER_TOKEN_3), Some(MultiplierOf::::from(MULTIPLIER_3)), AcceptedFundingAsset::USDT), + TestBid::new(BIDDER_4, TOKEN_AMOUNT_4, PriceOf::::from_float(PRICE_PER_TOKEN_4), Some(MultiplierOf::::from(MULTIPLIER_4)), AcceptedFundingAsset::USDT), + TestBid::new(BIDDER_5, TOKEN_AMOUNT_5, PriceOf::::from_float(PRICE_PER_TOKEN_5), Some(MultiplierOf::::from(MULTIPLIER_5)), AcceptedFundingAsset::USDT), + ]; + + let expected_plmc_spent = vec![ + (BIDDER_1, EXPECTED_PLMC_AMOUNT_1), + (BIDDER_2, EXPECTED_PLMC_AMOUNT_2), + (BIDDER_3, EXPECTED_PLMC_AMOUNT_3), + (BIDDER_4, EXPECTED_PLMC_AMOUNT_4), + (BIDDER_5, EXPECTED_PLMC_AMOUNT_5), + ]; + + let result = super::helper_functions::calculate_auction_plmc_spent(bids); + assert_eq!(result, expected_plmc_spent); + } + + #[test] + fn calculate_contributed_plmc_spent() { + const PLMC_PRICE: f64 = 8.4f64; + const CT_PRICE: f64 = 16.32f64; + + const CONTRIBUTOR_1: AccountIdOf = 1u64; + const TOKEN_AMOUNT_1: u128 = 120_0_000_000_000_u128; + const MULTIPLIER_1: u32 = 1u32; + const TICKET_SIZE_USD_1: u128 = 1_958_4_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_1: u128 = 233_1_428_571_428_u128; + + const CONTRIBUTOR_2: AccountIdOf = 2u64; + const TOKEN_AMOUNT_2: u128 = 5023_0_000_000_000_u128; + const MULTIPLIER_2: u32 = 2u32; + const TICKET_SIZE_USD_2: u128 = 81_975_3_600_000_000_u128; + const EXPECTED_PLMC_AMOUNT_2: u128 = 4_879_4_857_142_857_u128; + + const CONTRIBUTOR_3: AccountIdOf = 3u64; + const TOKEN_AMOUNT_3: u128 = 20_000_0_000_000_000_u128; + const MULTIPLIER_3: u32 = 17u32; + const TICKET_SIZE_USD_3: u128 = 326_400_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_3: u128 = 2_285_7_142_857_142_u128; + + const CONTRIBUTOR_4: AccountIdOf = 4u64; + const TOKEN_AMOUNT_4: u128 = 1_000_000_0_000_000_000_u128; + const MULTIPLIER_4: u32 = 25u32; + const TICKET_SIZE_4: u128 = 16_320_000_0_000_000_000_u128; + const EXPECTED_PLMC_AMOUNT_4: u128 = 77_714_2_857_142_857_u128; + + const CONTRIBUTOR_5: AccountIdOf = 5u64; + const TOKEN_AMOUNT_5: u128 = 0_1_233_000_000_u128; + const MULTIPLIER_5: u32 = 10u32; + const TICKET_SIZE_5: u128 = 2_0_122_562_000_u128; + const EXPECTED_PLMC_AMOUNT_5: u128 = 0_0_239_554_285_u128; + + + + assert_eq!( + ::PriceProvider::get_price(PLMC_STATEMINT_ID.into()).unwrap(), + PriceOf::::from_float(PLMC_PRICE) + ); + + let contributions = vec![ + TestContribution::new(CONTRIBUTOR_1, TOKEN_AMOUNT_1, Some(MultiplierOf::::from(MULTIPLIER_1)), AcceptedFundingAsset::USDT), + TestContribution::new(CONTRIBUTOR_2, TOKEN_AMOUNT_2, Some(MultiplierOf::::from(MULTIPLIER_2)), AcceptedFundingAsset::USDT), + TestContribution::new(CONTRIBUTOR_3, TOKEN_AMOUNT_3, Some(MultiplierOf::::from(MULTIPLIER_3)), AcceptedFundingAsset::USDT), + TestContribution::new(CONTRIBUTOR_4, TOKEN_AMOUNT_4, Some(MultiplierOf::::from(MULTIPLIER_4)), AcceptedFundingAsset::USDT), + TestContribution::new(CONTRIBUTOR_5, TOKEN_AMOUNT_5, Some(MultiplierOf::::from(MULTIPLIER_5)), AcceptedFundingAsset::USDT), + ]; + + let expected_plmc_spent = vec![ + (CONTRIBUTOR_1, EXPECTED_PLMC_AMOUNT_1), + (CONTRIBUTOR_2, EXPECTED_PLMC_AMOUNT_2), + (CONTRIBUTOR_3, EXPECTED_PLMC_AMOUNT_3), + (CONTRIBUTOR_4, EXPECTED_PLMC_AMOUNT_4), + (CONTRIBUTOR_5, EXPECTED_PLMC_AMOUNT_5), + ]; + + let result = super::helper_functions::calculate_contributed_plmc_spent(contributions, PriceOf::::from_float(CT_PRICE)); + assert_eq!(result, expected_plmc_spent); + } +} + #[cfg(test)] mod misc_features { use super::*; @@ -3199,11 +3381,11 @@ mod misc_features { #[test] fn sandbox() { - // let plmc_price_in_usd = 8_5_000_000_000u128; + // let plmc_price_in_usd = 8_5_000_000_000_u128; // let token_amount= FixedU128::from_float(12.5); // let ticket_size: u128 = token_amount.checked_mul_int(plmc_price_in_usd).unwrap(); // - // let ticket_size = 250_0_000_000_000u128; + // let ticket_size = 250_0_000_000_000_u128; // let rate = FixedU128::from_float(8.5f64); // let inv_rate = rate.reciprocal().unwrap(); // let amount = inv_rate.checked_mul_int(ticket_size).unwrap(); From 29bfd52279ba7fa73ecedd2375c077844f54b581 Mon Sep 17 00:00:00 2001 From: Juan Ignacio RIos Date: Mon, 26 Jun 2023 10:59:34 +0200 Subject: [PATCH 7/9] chore(209): fix warnings --- polimec-skeleton/pallets/funding/src/tests.rs | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/polimec-skeleton/pallets/funding/src/tests.rs b/polimec-skeleton/pallets/funding/src/tests.rs index 866b190fe..bb376e3a6 100644 --- a/polimec-skeleton/pallets/funding/src/tests.rs +++ b/polimec-skeleton/pallets/funding/src/tests.rs @@ -941,7 +941,7 @@ impl<'a> RemainderFundingProject<'a> { .buy_for_retail_users(contributions.clone()) .expect("Contributing should work"); - test_env.do_reserved_plmc_assertions(plmc_contribution_deposits, LockType::Participation(project_id)); + test_env.do_reserved_plmc_assertions(total_plmc_participation_locked, LockType::Participation(project_id)); test_env.do_contribution_transferred_statemint_asset_assertions(funding_asset_deposits, project_id); test_env.do_free_plmc_assertions(expected_free_plmc_balances); test_env.do_free_statemint_asset_assertions(prev_funding_asset_balances); @@ -1265,7 +1265,7 @@ pub mod helper_functions { output = Vec::new(); let mut i = 0; let mut j = 0; - while true { + loop { let old_tup = old_output.get(i); let new_tup = map.get(j); @@ -1299,7 +1299,7 @@ pub mod helper_functions { } pub fn generic_map_merge_reduce( - mut mappings: Vec>, key_extractor: impl Fn(&M) -> K, initial_state: S, merge_reduce: impl Fn(&M, S) -> S, + mappings: Vec>, key_extractor: impl Fn(&M) -> K, initial_state: S, merge_reduce: impl Fn(&M, S) -> S, ) -> Vec<(K, S)> { let mut output = BTreeMap::new(); for mut map in mappings { @@ -1360,8 +1360,8 @@ pub mod helper_functions { // Mappings should be sorted based on their account id, ascending. pub fn merge_subtract_mappings_by_user( - mut base_mapping: Vec<(AccountIdOf, I)>, - mut subtract_mappings: Vec, I)>>, + base_mapping: Vec<(AccountIdOf, I)>, + subtract_mappings: Vec, I)>>, ) -> Vec<(AccountIdOf, I)> { let mut output = base_mapping; output.sort_by_key(|k| k.0); @@ -1371,7 +1371,7 @@ pub mod helper_functions { output = Vec::new(); let mut i = 0; let mut j = 0; - while true { + loop { let old_tup = old_output.get(i); let new_tup = map.get(j); @@ -1777,7 +1777,7 @@ mod auction_round_success { ); let mut bids = Vec::new(); - for i in 0..::MaxBidsPerUser::get() { + for _ in 0..::MaxBidsPerUser::get() { bids.push(TestBid::new( evaluator_bidder, 10 * ASSET_UNIT, @@ -1787,7 +1787,7 @@ mod auction_round_success { )); } - let mut fill_necessary_plmc_for_bids = calculate_auction_plmc_spent(bids.clone()); + let fill_necessary_plmc_for_bids = calculate_auction_plmc_spent(bids.clone()); let fill_necessary_usdt_for_bids = calculate_auction_funding_asset_spent(bids.clone()); let bid_necessary_plmc = calculate_auction_plmc_spent(vec![evaluator_bid]); @@ -2016,7 +2016,7 @@ mod auction_round_success { let ed_plmc: UserToPLMCBalance = evaluations .clone() .into_iter() - .map(|(account, amount)| (account, get_ed())) + .map(|(account, _amount)| (account, get_ed())) .collect(); test_env.mint_plmc_to(required_plmc); test_env.mint_plmc_to(ed_plmc); @@ -2042,7 +2042,7 @@ mod auction_round_success { let ed_plmc: UserToPLMCBalance = evaluations .clone() .into_iter() - .map(|(account, amount)| (account, get_ed())) + .map(|(account, _amount)| (account, get_ed())) .collect(); test_env.mint_plmc_to(required_plmc); test_env.mint_plmc_to(ed_plmc); @@ -2072,7 +2072,7 @@ mod auction_round_success { let ed_plmc: UserToPLMCBalance = evaluations .clone() .into_iter() - .map(|(account, amount)| (account, get_ed())) + .map(|(account, _amount)| (account, get_ed())) .collect(); test_env.mint_plmc_to(required_plmc); test_env.mint_plmc_to(ed_plmc); @@ -2758,7 +2758,7 @@ mod community_round_success { ); let mut fill_contributions = Vec::new(); - for i in 0..::MaxContributionsPerUser::get() { + for _i in 0..::MaxContributionsPerUser::get() { fill_contributions.push(TestContribution::new( evaluator_contributor, 10 * ASSET_UNIT, @@ -2768,7 +2768,7 @@ mod community_round_success { } let expected_price = FixedU128::from_float(38.3333333333f64); - let mut fill_necessary_plmc = calculate_contributed_plmc_spent(fill_contributions.clone(), expected_price); + let fill_necessary_plmc = calculate_contributed_plmc_spent(fill_contributions.clone(), expected_price); let fill_necessary_usdt_for_bids = calculate_contributed_funding_asset_spent(fill_contributions.clone(), expected_price); @@ -2901,7 +2901,7 @@ mod remainder_round_success { ); let mut fill_contributions = Vec::new(); - for i in 0..::MaxContributionsPerUser::get() { + for _i in 0..::MaxContributionsPerUser::get() { fill_contributions.push(TestContribution::new( evaluator_contributor, 10 * ASSET_UNIT, @@ -2911,7 +2911,7 @@ mod remainder_round_success { } let expected_price = FixedU128::from_float(38.3333333333f64); - let mut fill_necessary_plmc = calculate_contributed_plmc_spent(fill_contributions.clone(), expected_price); + let fill_necessary_plmc = calculate_contributed_plmc_spent(fill_contributions.clone(), expected_price); let fill_necessary_usdt_for_bids = calculate_contributed_funding_asset_spent(fill_contributions.clone(), expected_price); @@ -2986,7 +2986,7 @@ mod purchased_vesting { let user_buys = generic_map_merge( vec![community_contributions.clone(), default_remainder_buys()], |m| m.contributor.clone(), - |mut m1, m2| { + |m1, m2| { let total_amount = m1.amount.clone() + m2.amount.clone(); let mut mx = m1.clone(); mx.amount = total_amount; @@ -3226,35 +3226,35 @@ mod test_helper_functions { const TOKEN_AMOUNT_1: u128 = 120_0_000_000_000_u128; const PRICE_PER_TOKEN_1: f64 = 0.3f64; const MULTIPLIER_1: u32 = 1u32; - const TICKET_SIZE_USD_1: u128 = 36_0_000_000_000_u128; + const _TICKET_SIZE_USD_1: u128 = 36_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_1: u128 = 4_2_857_142_857_u128; const BIDDER_2: AccountIdOf = 2u64; const TOKEN_AMOUNT_2: u128 = 5023_0_000_000_000_u128; const PRICE_PER_TOKEN_2: f64 = 13f64; const MULTIPLIER_2: u32 = 2u32; - const TICKET_SIZE_USD_2: u128 = 65_299_0_000_000_000_u128; + const _TICKET_SIZE_USD_2: u128 = 65_299_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_2: u128 = 3_886_8_452_380_952_u128; const BIDDER_3: AccountIdOf = 3u64; const TOKEN_AMOUNT_3: u128 = 20_000_0_000_000_000_u128; const PRICE_PER_TOKEN_3: f64 = 20f64; const MULTIPLIER_3: u32 = 17u32; - const TICKET_SIZE_USD_3: u128 = 400_000_0_000_000_000_u128; + const _TICKET_SIZE_USD_3: u128 = 400_000_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_3: u128 = 2_801_1_204_481_792_u128; const BIDDER_4: AccountIdOf = 4u64; const TOKEN_AMOUNT_4: u128 = 1_000_000_0_000_000_000_u128; const PRICE_PER_TOKEN_4: f64 = 5.52f64; const MULTIPLIER_4: u32 = 25u32; - const TICKET_SIZE_4: u128 = 5_520_000_0_000_000_000_u128; + const _TICKET_SIZE_USD_4: u128 = 5_520_000_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_4: u128 = 26_285_7_142_857_142_u128; const BIDDER_5: AccountIdOf = 5u64; const TOKEN_AMOUNT_5: u128 = 0_1_233_000_000_u128; const PRICE_PER_TOKEN_5: f64 = 11.34f64; const MULTIPLIER_5: u32 = 10u32; - const TICKET_SIZE_5: u128 = 1_3_982_220_000_u128; + const _TICKET_SIZE_USD_5: u128 = 1_3_982_220_000_u128; // TODO: Is this due to rounding errors? // Should be in reality 0.0166455, but we get 0.0166454999. i.e error of 0.0000000001 PLMC const EXPECTED_PLMC_AMOUNT_5: u128 = 0_0_166_454_999_u128; @@ -3294,31 +3294,31 @@ mod test_helper_functions { const CONTRIBUTOR_1: AccountIdOf = 1u64; const TOKEN_AMOUNT_1: u128 = 120_0_000_000_000_u128; const MULTIPLIER_1: u32 = 1u32; - const TICKET_SIZE_USD_1: u128 = 1_958_4_000_000_000_u128; + const _TICKET_SIZE_USD_1: u128 = 1_958_4_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_1: u128 = 233_1_428_571_428_u128; const CONTRIBUTOR_2: AccountIdOf = 2u64; const TOKEN_AMOUNT_2: u128 = 5023_0_000_000_000_u128; const MULTIPLIER_2: u32 = 2u32; - const TICKET_SIZE_USD_2: u128 = 81_975_3_600_000_000_u128; + const _TICKET_SIZE_USD_2: u128 = 81_975_3_600_000_000_u128; const EXPECTED_PLMC_AMOUNT_2: u128 = 4_879_4_857_142_857_u128; const CONTRIBUTOR_3: AccountIdOf = 3u64; const TOKEN_AMOUNT_3: u128 = 20_000_0_000_000_000_u128; const MULTIPLIER_3: u32 = 17u32; - const TICKET_SIZE_USD_3: u128 = 326_400_0_000_000_000_u128; + const _TICKET_SIZE_USD_3: u128 = 326_400_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_3: u128 = 2_285_7_142_857_142_u128; const CONTRIBUTOR_4: AccountIdOf = 4u64; const TOKEN_AMOUNT_4: u128 = 1_000_000_0_000_000_000_u128; const MULTIPLIER_4: u32 = 25u32; - const TICKET_SIZE_4: u128 = 16_320_000_0_000_000_000_u128; + const _TICKET_SIZE_4: u128 = 16_320_000_0_000_000_000_u128; const EXPECTED_PLMC_AMOUNT_4: u128 = 77_714_2_857_142_857_u128; const CONTRIBUTOR_5: AccountIdOf = 5u64; const TOKEN_AMOUNT_5: u128 = 0_1_233_000_000_u128; const MULTIPLIER_5: u32 = 10u32; - const TICKET_SIZE_5: u128 = 2_0_122_562_000_u128; + const _TICKET_SIZE_5: u128 = 2_0_122_562_000_u128; const EXPECTED_PLMC_AMOUNT_5: u128 = 0_0_239_554_285_u128; From 77d7d8b46cce907d195d846a1ad6bae414833898 Mon Sep 17 00:00:00 2001 From: Juan Ignacio RIos Date: Mon, 26 Jun 2023 11:14:20 +0200 Subject: [PATCH 8/9] chore(209): fat finger --- polimec-skeleton/pallets/funding/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polimec-skeleton/pallets/funding/src/lib.rs b/polimec-skeleton/pallets/funding/src/lib.rs index d3da3da87..edc43470d 100644 --- a/polimec-skeleton/pallets/funding/src/lib.rs +++ b/polimec-skeleton/pallets/funding/src/lib.rs @@ -274,7 +274,7 @@ pub mod pallet { /// The inner balance type we will use for all of our outer currency types. (e.g native, funding, CTs) type Balance: Balance + From + FixedPointOperand; - /// Represents the value of something in USDf + /// Represents the value of something in USD type Price: FixedPointNumber + Parameter + Copy; /// The chains native currency From f5de088934f6e916cb29546622a34a7b2d3a8dbe Mon Sep 17 00:00:00 2001 From: Juan Ignacio RIos Date: Mon, 26 Jun 2023 16:31:03 +0200 Subject: [PATCH 9/9] refactor(209): abstracted lock and release of plmc and funding assets on bid and contribute extrinsics --- .../pallets/funding/src/functions.rs | 237 ++++++++---------- polimec-skeleton/pallets/funding/src/tests.rs | 147 ++++++++--- 2 files changed, 212 insertions(+), 172 deletions(-) diff --git a/polimec-skeleton/pallets/funding/src/functions.rs b/polimec-skeleton/pallets/funding/src/functions.rs index 02c9f7b08..b291a13f0 100644 --- a/polimec-skeleton/pallets/funding/src/functions.rs +++ b/polimec-skeleton/pallets/funding/src/functions.rs @@ -769,14 +769,13 @@ impl Pallet { } Err(_) => { // Evaluations are stored in descending order. If the evaluation vector for the user is full, we drop the lowest/last bond - let lowest_evaluation_index: usize = (T::MaxEvaluationsPerUser::get() - 1) - .try_into() - .map_err(|_| Error::::BadMath)?; - let lowest_evaluation = existing_evaluations.swap_remove(lowest_evaluation_index); + let lowest_evaluation = existing_evaluations.swap_remove(existing_evaluations.len() - 1); + ensure!( lowest_evaluation.plmc_bond < plmc_bond, Error::::EvaluationBondTooLow ); + T::NativeCurrency::release( &LockType::Evaluation(project_id), &lowest_evaluation.evaluator, @@ -875,8 +874,6 @@ impl Pallet { let now = >::block_number(); let bid_id = Self::next_bid_id(); let mut existing_bids = Bids::::get(project_id, bidder.clone()); - let evaluation_bonded = - ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &bidder); let ticket_size = ct_usd_price.checked_mul_int(ct_amount).ok_or(Error::::BadMath)?; let funding_asset_usd_price = @@ -907,7 +904,6 @@ impl Pallet { ); // * Calculate new variables * - let holding_account = Self::fund_account_id(project_id); let (plmc_vesting_period, ct_vesting_period) = Self::calculate_vesting_periods(bidder.clone(), multiplier, ct_amount, ct_usd_price) .map_err(|_| Error::::BadMath)?; @@ -937,76 +933,37 @@ impl Pallet { // * Update storage * match existing_bids.try_push(new_bid.clone()) { Ok(_) => { - let new_bond_to_lock = required_plmc_bond.saturating_sub(evaluation_bonded); - let evaluation_bonded_to_change_lock = required_plmc_bond.saturating_sub(new_bond_to_lock); + Self::try_plmc_participation_lock(&bidder, project_id, required_plmc_bond)?; + Self::try_funding_asset_hold(&bidder, project_id, required_funding_asset_transfer, asset_id)?; - T::NativeCurrency::release( - &LockType::Evaluation(project_id), - &bidder, - evaluation_bonded_to_change_lock, - Precision::Exact, - ) - .map_err(|_| Error::::ImpossibleState)?; - - T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) - .map_err(|_| Error::::InsufficientBalance)?; - - T::FundingCurrency::transfer( - asset_id.into(), - &bidder, - &holding_account, - required_funding_asset_transfer, - Preservation::Expendable, - )?; // TODO: PLMC-159. Send an XCM message to Statemint/e to transfer a `bid.market_cap` amount of USDC (or the Currency specified by the issuer) to the PalletId Account // Alternative TODO: PLMC-159. The user should have the specified currency (e.g: USDC) already on Polimec } Err(_) => { // Since the bids are sorted by price, and in this branch the Vec is full, the last element is the lowest bid - let lowest_bid_index: usize = (T::MaxBidsPerUser::get() - 1) - .try_into() - .map_err(|_| Error::::BadMath)?; - let lowest_bid = existing_bids.swap_remove(lowest_bid_index); - - ensure!(new_bid.clone() > lowest_bid, Error::::BidTooLow); - - let unlocked_bond = T::NativeCurrency::release( - &LockType::Participation(project_id), - &lowest_bid.bidder.clone(), - lowest_bid.plmc_bond, - Precision::Exact, - )?; - - let new_required_plmc_bond = required_plmc_bond.saturating_sub(unlocked_bond); + let lowest_plmc_bond = existing_bids + .iter() + .last() + .ok_or(Error::::ImpossibleState)? + .plmc_bond; - let new_bond_to_lock = new_required_plmc_bond.saturating_sub(evaluation_bonded); - let evaluation_bonded_to_change_lock = new_required_plmc_bond.saturating_sub(new_bond_to_lock); + ensure!( + new_bid.plmc_bond > lowest_plmc_bond, + Error::::BidTooLow + ); - T::NativeCurrency::release( - &LockType::Evaluation(project_id), + Self::release_last_funding_item_in_vec( &bidder, - evaluation_bonded_to_change_lock, - Precision::Exact, - ) - .map_err(|_| Error::::ImpossibleState)?; + project_id, + asset_id, + &mut existing_bids, + |x| x.plmc_bond, + |x| x.funding_asset_amount, + )?; - T::NativeCurrency::hold(&LockType::Participation(project_id), &bidder, required_plmc_bond) - .map_err(|_| Error::::InsufficientBalance)?; + Self::try_plmc_participation_lock(&bidder, project_id, required_plmc_bond)?; - T::FundingCurrency::transfer( - lowest_bid.funding_asset.to_statemint_id(), - &holding_account, - &lowest_bid.bidder.clone(), - lowest_bid.funding_asset_amount, - Preservation::Expendable, - )?; - T::FundingCurrency::transfer( - asset_id.into(), - &bidder, - &holding_account, - required_funding_asset_transfer, - Preservation::Expendable, - )?; + Self::try_funding_asset_hold(&bidder, project_id, required_funding_asset_transfer, asset_id)?; // This should never fail, since we just removed an element from the Vec existing_bids @@ -1054,8 +1011,6 @@ impl Pallet { let now = >::block_number(); let contribution_id = Self::next_contribution_id(); let mut existing_contributions = Contributions::::get(project_id, contributor.clone()); - let evaluation_bonded = - ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), &contributor); let ct_usd_price = project_details .weighted_average_price @@ -1076,6 +1031,7 @@ impl Pallet { || project_details.status == ProjectStatus::RemainderRound, Error::::AuctionNotStarted ); + if let Some(minimum_ticket_size) = project_metadata.ticket_size.minimum { // Make sure the bid amount is greater than the minimum specified by the issuer ensure!(ticket_size >= minimum_ticket_size, Error::::ContributionTooLow); @@ -1096,7 +1052,6 @@ impl Pallet { // ); // * Calculate variables * - let fund_account = Self::fund_account_id(project_id); let buyable_tokens = if project_details.remaining_contribution_tokens > token_amount { token_amount } else { @@ -1115,7 +1070,7 @@ impl Pallet { .ok_or(Error::::BadMath)? .saturating_mul_int(ticket_size); let asset_id = asset.to_statemint_id(); - let mut remaining_cts_after_purchase = project_details + let remaining_cts_after_purchase = project_details .remaining_contribution_tokens .saturating_sub(buyable_tokens); @@ -1136,80 +1091,34 @@ impl Pallet { // Try adding the new contribution to the system match existing_contributions.try_push(new_contribution.clone()) { Ok(_) => { - let new_bond_to_lock = required_plmc_bond.saturating_sub(evaluation_bonded); - let evaluation_bonded_to_change_lock = required_plmc_bond.saturating_sub(new_bond_to_lock); - - T::NativeCurrency::release( - &LockType::Evaluation(project_id), - &contributor, - evaluation_bonded_to_change_lock, - Precision::Exact, - ) - .map_err(|_| Error::::ImpossibleState)?; - - T::NativeCurrency::hold(&LockType::Participation(project_id), &contributor, required_plmc_bond) - .map_err(|_| Error::::InsufficientBalance)?; - - T::FundingCurrency::transfer( - asset_id.into(), - &contributor, - &fund_account, - required_funding_asset_transfer, - Preservation::Expendable, - )?; + Self::try_plmc_participation_lock(&contributor, project_id, required_plmc_bond)?; + Self::try_funding_asset_hold(&contributor, project_id, required_funding_asset_transfer, asset_id)?; } Err(_) => { // The contributions are sorted by highest PLMC bond. If the contribution vector for the user is full, we drop the lowest/last item - let lowest_contribution_index: usize = (T::MaxContributionsPerUser::get() - 1) - .try_into() - .map_err(|_| Error::::BadMath)?; - let lowest_contribution = existing_contributions.swap_remove(lowest_contribution_index); - remaining_cts_after_purchase = - remaining_cts_after_purchase.saturating_add(lowest_contribution.ct_amount); + let lowest_plmc_bond = existing_contributions + .iter() + .last() + .ok_or(Error::::ImpossibleState)? + .plmc_bond; ensure!( - new_contribution.plmc_bond > lowest_contribution.plmc_bond, + new_contribution.plmc_bond > lowest_plmc_bond, Error::::ContributionTooLow ); - let unlocked_bond = T::NativeCurrency::release( - &LockType::Participation(project_id), - &lowest_contribution.contributor, - lowest_contribution.plmc_bond, - Precision::Exact, - )?; - - let new_required_plmc_bond = required_plmc_bond.saturating_sub(unlocked_bond); - - let new_bond_to_lock = new_required_plmc_bond.saturating_sub(evaluation_bonded); - let evaluation_bonded_to_change_lock = new_required_plmc_bond.saturating_sub(new_bond_to_lock); - - T::NativeCurrency::release( - &LockType::Evaluation(project_id), + Self::release_last_funding_item_in_vec( &contributor, - evaluation_bonded_to_change_lock, - Precision::Exact, - ) - .map_err(|_| Error::::ImpossibleState)?; - - T::NativeCurrency::hold(&LockType::Participation(project_id), &contributor, required_plmc_bond) - .map_err(|_| Error::::InsufficientBalance)?; - - T::FundingCurrency::transfer( - asset_id.into(), - &fund_account, - &contributor, - lowest_contribution.funding_asset_amount, - Preservation::Expendable, + project_id, + asset_id, + &mut existing_contributions, + |x| x.plmc_bond, + |x| x.funding_asset_amount, )?; - T::FundingCurrency::transfer( - asset_id.into(), - &contributor, - &fund_account, - required_funding_asset_transfer, - Preservation::Expendable, - )?; + Self::try_plmc_participation_lock(&contributor, project_id, required_plmc_bond)?; + + Self::try_funding_asset_hold(&contributor, project_id, required_funding_asset_transfer, asset_id)?; // This should never fail, since we just removed an item from the vector existing_contributions @@ -1769,4 +1678,66 @@ impl Pallet { let zeroes: BalanceOf = BalanceOf::::from(10u64).saturating_pow(decimals.into()); number.saturating_mul(zeroes) } + + pub fn try_plmc_participation_lock( + who: &T::AccountId, project_id: T::ProjectIdentifier, amount: BalanceOf, + ) -> Result<(), DispatchError> { + // Check if the user has already locked tokens in the evaluation period + let evaluation_bonded = ::NativeCurrency::balance_on_hold(&LockType::Evaluation(project_id), who); + + let new_amount_to_lock = amount.saturating_sub(evaluation_bonded); + let evaluation_bonded_to_change_lock = amount.saturating_sub(new_amount_to_lock); + + T::NativeCurrency::release( + &LockType::Evaluation(project_id), + who, + evaluation_bonded_to_change_lock, + Precision::Exact, + ) + .map_err(|_| Error::::ImpossibleState)?; + + T::NativeCurrency::hold(&LockType::Participation(project_id), who, amount) + .map_err(|_| Error::::InsufficientBalance)?; + + Ok(()) + } + + // TODO(216): use the hold interface of the fungibles::MutateHold once its implemented on pallet_assets. + pub fn try_funding_asset_hold( + who: &T::AccountId, project_id: T::ProjectIdentifier, amount: BalanceOf, asset_id: AssetIdOf, + ) -> Result<(), DispatchError> { + let fund_account = Self::fund_account_id(project_id); + + T::FundingCurrency::transfer(asset_id, &who, &fund_account, amount, Preservation::Expendable)?; + + Ok(()) + } + + // TODO(216): use the hold interface of the fungibles::MutateHold once its implemented on pallet_assets. + pub fn release_last_funding_item_in_vec( + who: &T::AccountId, project_id: T::ProjectIdentifier, asset_id: AssetIdOf, vec: &mut BoundedVec, + plmc_getter: impl Fn(&I) -> BalanceOf, funding_asset_getter: impl Fn(&I) -> BalanceOf, + ) -> Result<(), DispatchError> { + let fund_account = Self::fund_account_id(project_id); + let last_item = vec.swap_remove(vec.len() - 1); + let plmc_amount = plmc_getter(&last_item); + let funding_asset_amount = funding_asset_getter(&last_item); + + T::NativeCurrency::release( + &LockType::Participation(project_id), + &who, + plmc_amount, + Precision::Exact, + )?; + + T::FundingCurrency::transfer( + asset_id, + &fund_account, + &who, + funding_asset_amount, + Preservation::Expendable, + )?; + + Ok(()) + } } diff --git a/polimec-skeleton/pallets/funding/src/tests.rs b/polimec-skeleton/pallets/funding/src/tests.rs index bb376e3a6..7a29701c4 100644 --- a/polimec-skeleton/pallets/funding/src/tests.rs +++ b/polimec-skeleton/pallets/funding/src/tests.rs @@ -1176,8 +1176,8 @@ mod defaults { } pub mod helper_functions { - use std::collections::BTreeMap; use super::*; + use std::collections::BTreeMap; pub fn get_ed() -> BalanceOf { ::ExistentialDeposit::get() @@ -1360,8 +1360,7 @@ pub mod helper_functions { // Mappings should be sorted based on their account id, ascending. pub fn merge_subtract_mappings_by_user( - base_mapping: Vec<(AccountIdOf, I)>, - subtract_mappings: Vec, I)>>, + base_mapping: Vec<(AccountIdOf, I)>, subtract_mappings: Vec, I)>>, ) -> Vec<(AccountIdOf, I)> { let mut output = base_mapping; output.sort_by_key(|k| k.0); @@ -2176,11 +2175,35 @@ mod auction_round_failure { let project_id = auctioning_project.project_id; const DAVE: AccountId = 42; let bids: TestBids = vec![ - TestBid::new(DAVE, 10_000 * USDT_UNIT, 2_u128.into(), None, AcceptedFundingAsset::USDT), // 20k - TestBid::new(DAVE, 12_000 * USDT_UNIT, 8_u128.into(), None, AcceptedFundingAsset::USDT), // 96k - TestBid::new(DAVE, 15_000 * USDT_UNIT, 5_u128.into(), None, AcceptedFundingAsset::USDT), // 75k - TestBid::new(DAVE, 1_000 * USDT_UNIT, 7_u128.into(), None, AcceptedFundingAsset::USDT), // 7k - TestBid::new(DAVE, 20_000 * USDT_UNIT, 5_u128.into(), None, AcceptedFundingAsset::USDT), // 100k + TestBid::new( + DAVE, + 10_000 * USDT_UNIT, + 2_u128.into(), + None, + AcceptedFundingAsset::USDT, + ), // 20k + TestBid::new( + DAVE, + 12_000 * USDT_UNIT, + 8_u128.into(), + None, + AcceptedFundingAsset::USDT, + ), // 96k + TestBid::new( + DAVE, + 15_000 * USDT_UNIT, + 5_u128.into(), + None, + AcceptedFundingAsset::USDT, + ), // 75k + TestBid::new(DAVE, 1_000 * USDT_UNIT, 7_u128.into(), None, AcceptedFundingAsset::USDT), // 7k + TestBid::new( + DAVE, + 20_000 * USDT_UNIT, + 5_u128.into(), + None, + AcceptedFundingAsset::USDT, + ), // 100k ]; let mut plmc_fundings: UserToPLMCBalance = calculate_auction_plmc_spent(bids.clone()); @@ -3041,33 +3064,23 @@ mod purchased_vesting { }, ); - let participation_locked_plmc = merge_add_mappings_by_user(vec![bidders_plmc_bond.clone(), contributors_plmc_spent.clone()]); - let purchase_unbonds = merge_subtract_mappings_by_user(participation_locked_plmc.clone(), vec![bidders_plmc_bond.clone()]); - - + let participation_locked_plmc = + merge_add_mappings_by_user(vec![bidders_plmc_bond.clone(), contributors_plmc_spent.clone()]); + let purchase_unbonds = + merge_subtract_mappings_by_user(participation_locked_plmc.clone(), vec![bidders_plmc_bond.clone()]); for ((user, pre_locked), (_, post_released)) in zip(participation_locked_plmc, purchase_unbonds) { let actual_bonded_plmc = test_env.in_ext(|| { - ::NativeCurrency::balance_on_hold( - &LockType::Participation(project_id), - &user, - ) + ::NativeCurrency::balance_on_hold(&LockType::Participation(project_id), &user) }); assert_eq!(actual_bonded_plmc, pre_locked); let result = test_env.in_ext(|| { - FundingModule::vested_plmc_purchase_unbond_for( - RuntimeOrigin::signed(user), - project_id, - user, - ) + FundingModule::vested_plmc_purchase_unbond_for(RuntimeOrigin::signed(user), project_id, user) }); let actual_bonded_plmc = test_env.in_ext(|| { - ::NativeCurrency::balance_on_hold( - &LockType::Participation(project_id), - &user, - ) + ::NativeCurrency::balance_on_hold(&LockType::Participation(project_id), &user) }); assert_ok!(result); assert_eq!(actual_bonded_plmc, pre_locked - post_released); @@ -3267,11 +3280,41 @@ mod test_helper_functions { ); let bids = vec![ - TestBid::new(BIDDER_1, TOKEN_AMOUNT_1, PriceOf::::from_float(PRICE_PER_TOKEN_1), Some(MultiplierOf::::from(MULTIPLIER_1)), AcceptedFundingAsset::USDT), - TestBid::new(BIDDER_2, TOKEN_AMOUNT_2, PriceOf::::from_float(PRICE_PER_TOKEN_2), Some(MultiplierOf::::from(MULTIPLIER_2)), AcceptedFundingAsset::USDT), - TestBid::new(BIDDER_3, TOKEN_AMOUNT_3, PriceOf::::from_float(PRICE_PER_TOKEN_3), Some(MultiplierOf::::from(MULTIPLIER_3)), AcceptedFundingAsset::USDT), - TestBid::new(BIDDER_4, TOKEN_AMOUNT_4, PriceOf::::from_float(PRICE_PER_TOKEN_4), Some(MultiplierOf::::from(MULTIPLIER_4)), AcceptedFundingAsset::USDT), - TestBid::new(BIDDER_5, TOKEN_AMOUNT_5, PriceOf::::from_float(PRICE_PER_TOKEN_5), Some(MultiplierOf::::from(MULTIPLIER_5)), AcceptedFundingAsset::USDT), + TestBid::new( + BIDDER_1, + TOKEN_AMOUNT_1, + PriceOf::::from_float(PRICE_PER_TOKEN_1), + Some(MultiplierOf::::from(MULTIPLIER_1)), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_2, + TOKEN_AMOUNT_2, + PriceOf::::from_float(PRICE_PER_TOKEN_2), + Some(MultiplierOf::::from(MULTIPLIER_2)), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_3, + TOKEN_AMOUNT_3, + PriceOf::::from_float(PRICE_PER_TOKEN_3), + Some(MultiplierOf::::from(MULTIPLIER_3)), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_4, + TOKEN_AMOUNT_4, + PriceOf::::from_float(PRICE_PER_TOKEN_4), + Some(MultiplierOf::::from(MULTIPLIER_4)), + AcceptedFundingAsset::USDT, + ), + TestBid::new( + BIDDER_5, + TOKEN_AMOUNT_5, + PriceOf::::from_float(PRICE_PER_TOKEN_5), + Some(MultiplierOf::::from(MULTIPLIER_5)), + AcceptedFundingAsset::USDT, + ), ]; let expected_plmc_spent = vec![ @@ -3285,7 +3328,7 @@ mod test_helper_functions { let result = super::helper_functions::calculate_auction_plmc_spent(bids); assert_eq!(result, expected_plmc_spent); } - + #[test] fn calculate_contributed_plmc_spent() { const PLMC_PRICE: f64 = 8.4f64; @@ -3321,19 +3364,42 @@ mod test_helper_functions { const _TICKET_SIZE_5: u128 = 2_0_122_562_000_u128; const EXPECTED_PLMC_AMOUNT_5: u128 = 0_0_239_554_285_u128; - - assert_eq!( ::PriceProvider::get_price(PLMC_STATEMINT_ID.into()).unwrap(), PriceOf::::from_float(PLMC_PRICE) ); let contributions = vec![ - TestContribution::new(CONTRIBUTOR_1, TOKEN_AMOUNT_1, Some(MultiplierOf::::from(MULTIPLIER_1)), AcceptedFundingAsset::USDT), - TestContribution::new(CONTRIBUTOR_2, TOKEN_AMOUNT_2, Some(MultiplierOf::::from(MULTIPLIER_2)), AcceptedFundingAsset::USDT), - TestContribution::new(CONTRIBUTOR_3, TOKEN_AMOUNT_3, Some(MultiplierOf::::from(MULTIPLIER_3)), AcceptedFundingAsset::USDT), - TestContribution::new(CONTRIBUTOR_4, TOKEN_AMOUNT_4, Some(MultiplierOf::::from(MULTIPLIER_4)), AcceptedFundingAsset::USDT), - TestContribution::new(CONTRIBUTOR_5, TOKEN_AMOUNT_5, Some(MultiplierOf::::from(MULTIPLIER_5)), AcceptedFundingAsset::USDT), + TestContribution::new( + CONTRIBUTOR_1, + TOKEN_AMOUNT_1, + Some(MultiplierOf::::from(MULTIPLIER_1)), + AcceptedFundingAsset::USDT, + ), + TestContribution::new( + CONTRIBUTOR_2, + TOKEN_AMOUNT_2, + Some(MultiplierOf::::from(MULTIPLIER_2)), + AcceptedFundingAsset::USDT, + ), + TestContribution::new( + CONTRIBUTOR_3, + TOKEN_AMOUNT_3, + Some(MultiplierOf::::from(MULTIPLIER_3)), + AcceptedFundingAsset::USDT, + ), + TestContribution::new( + CONTRIBUTOR_4, + TOKEN_AMOUNT_4, + Some(MultiplierOf::::from(MULTIPLIER_4)), + AcceptedFundingAsset::USDT, + ), + TestContribution::new( + CONTRIBUTOR_5, + TOKEN_AMOUNT_5, + Some(MultiplierOf::::from(MULTIPLIER_5)), + AcceptedFundingAsset::USDT, + ), ]; let expected_plmc_spent = vec![ @@ -3344,7 +3410,10 @@ mod test_helper_functions { (CONTRIBUTOR_5, EXPECTED_PLMC_AMOUNT_5), ]; - let result = super::helper_functions::calculate_contributed_plmc_spent(contributions, PriceOf::::from_float(CT_PRICE)); + let result = super::helper_functions::calculate_contributed_plmc_spent( + contributions, + PriceOf::::from_float(CT_PRICE), + ); assert_eq!(result, expected_plmc_spent); } }