diff --git a/pallets/funding/src/instantiator.rs b/pallets/funding/src/instantiator.rs
deleted file mode 100644
index 2163d3fb3..000000000
--- a/pallets/funding/src/instantiator.rs
+++ /dev/null
@@ -1,3279 +0,0 @@
-// Polimec Blockchain – https://www.polimec.org/
-// Copyright (C) Polimec 2022. All rights reserved.
-
-// The Polimec Blockchain is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// The Polimec Blockchain is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
-
-use crate::{
- traits::{BondingRequirementCalculation, ProvideAssetPrice},
- *,
-};
-use frame_support::{
- pallet_prelude::*,
- traits::{
- fungible::{Inspect as FungibleInspect, InspectHold as FungibleInspectHold, Mutate as FungibleMutate},
- fungibles::{
- metadata::Inspect as MetadataInspect, roles::Inspect as RolesInspect, Inspect as FungiblesInspect,
- Mutate as FungiblesMutate,
- },
- AccountTouch, Get, OnFinalize, OnIdle, OnInitialize,
- },
- weights::Weight,
- Parameter,
-};
-use frame_system::pallet_prelude::BlockNumberFor;
-use itertools::Itertools;
-use parity_scale_codec::Decode;
-use polimec_common::{credentials::InvestorType, migration_types::MigrationOrigin};
-#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))]
-use polimec_common_test_utils::generate_did_from_account;
-use sp_arithmetic::{
- traits::{SaturatedConversion, Saturating, Zero},
- FixedPointNumber, Percent, Perquintill,
-};
-use sp_runtime::{
- traits::{Convert, Member, One},
- DispatchError,
-};
-use sp_std::{
- cell::RefCell,
- collections::{btree_map::*, btree_set::*},
- iter::zip,
- marker::PhantomData,
-};
-
-pub type RuntimeOriginOf = ::RuntimeOrigin;
-pub struct BoxToFunction(pub Box);
-impl Default for BoxToFunction {
- fn default() -> Self {
- BoxToFunction(Box::new(|| ()))
- }
-}
-
-#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(
- feature = "std",
- serde(rename_all = "camelCase", deny_unknown_fields, bound(serialize = ""), bound(deserialize = ""))
-)]
-#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
-pub struct TestProjectParams {
- pub expected_state: ProjectStatus,
- pub metadata: ProjectMetadataOf,
- pub issuer: AccountIdOf,
- pub evaluations: Vec>,
- pub bids: Vec>,
- pub community_contributions: Vec>,
- pub remainder_contributions: Vec>,
-}
-
-#[cfg(feature = "std")]
-type OptionalExternalities = Option>;
-
-#[cfg(not(feature = "std"))]
-type OptionalExternalities = Option<()>;
-
-pub struct Instantiator<
- T: Config + pallet_balances::Config>,
- AllPalletsWithoutSystem: OnFinalize> + OnIdle> + OnInitialize>,
- RuntimeEvent: From> + TryInto> + Parameter + Member + IsType<::RuntimeEvent>,
-> {
- ext: OptionalExternalities,
- nonce: RefCell,
- _marker: PhantomData<(T, AllPalletsWithoutSystem, RuntimeEvent)>,
-}
-
-// general chain interactions
-impl<
- T: Config + pallet_balances::Config>,
- AllPalletsWithoutSystem: OnFinalize> + OnIdle> + OnInitialize>,
- RuntimeEvent: From> + TryInto> + Parameter + Member + IsType<::RuntimeEvent>,
- > Instantiator
-{
- pub fn new(ext: OptionalExternalities) -> Self {
- Self { ext, nonce: RefCell::new(0u64), _marker: PhantomData }
- }
-
- pub fn set_ext(&mut self, ext: OptionalExternalities) {
- self.ext = ext;
- }
-
- pub fn execute(&mut self, execution: impl FnOnce() -> R) -> R {
- #[cfg(feature = "std")]
- if let Some(ext) = &self.ext {
- return ext.borrow_mut().execute_with(execution);
- }
- execution()
- }
-
- pub fn get_new_nonce(&self) -> u64 {
- let nonce = *self.nonce.borrow_mut();
- self.nonce.replace(nonce + 1);
- nonce
- }
-
- pub fn get_free_plmc_balances_for(&mut self, user_keys: Vec>) -> Vec> {
- self.execute(|| {
- let mut balances: Vec> = Vec::new();
- for account in user_keys {
- let plmc_amount = ::NativeCurrency::balance(&account);
- balances.push(UserToPLMCBalance { account, plmc_amount });
- }
- balances.sort_by_key(|a| a.account.clone());
- balances
- })
- }
-
- pub fn get_reserved_plmc_balances_for(
- &mut self,
- user_keys: Vec>,
- lock_type: ::RuntimeHoldReason,
- ) -> Vec> {
- self.execute(|| {
- let mut balances: Vec> = Vec::new();
- for account in user_keys {
- let plmc_amount = ::NativeCurrency::balance_on_hold(&lock_type, &account);
- balances.push(UserToPLMCBalance { account, plmc_amount });
- }
- balances.sort_by(|a, b| a.account.cmp(&b.account));
- balances
- })
- }
-
- pub fn get_free_foreign_asset_balances_for(
- &mut self,
- asset_id: AssetIdOf,
- user_keys: Vec>,
- ) -> Vec> {
- self.execute(|| {
- let mut balances: Vec> = Vec::new();
- for account in user_keys {
- let asset_amount = ::FundingCurrency::balance(asset_id, &account);
- balances.push(UserToForeignAssets { account, asset_amount, asset_id });
- }
- balances.sort_by(|a, b| a.account.cmp(&b.account));
- balances
- })
- }
-
- pub fn get_ct_asset_balances_for(
- &mut self,
- project_id: ProjectId,
- user_keys: Vec>,
- ) -> Vec> {
- self.execute(|| {
- let mut balances: Vec> = Vec::new();
- for account in user_keys {
- let asset_amount = ::ContributionTokenCurrency::balance(project_id, &account);
- balances.push(asset_amount);
- }
- balances
- })
- }
-
- pub fn get_all_free_plmc_balances(&mut self) -> Vec> {
- let user_keys = self.execute(|| frame_system::Account::::iter_keys().collect());
- self.get_free_plmc_balances_for(user_keys)
- }
-
- pub fn get_all_reserved_plmc_balances(
- &mut self,
- reserve_type: ::RuntimeHoldReason,
- ) -> Vec> {
- let user_keys = self.execute(|| frame_system::Account::::iter_keys().collect());
- self.get_reserved_plmc_balances_for(user_keys, reserve_type)
- }
-
- pub fn get_all_free_foreign_asset_balances(&mut self, asset_id: AssetIdOf) -> Vec> {
- let user_keys = self.execute(|| frame_system::Account::::iter_keys().collect());
- self.get_free_foreign_asset_balances_for(asset_id, user_keys)
- }
-
- pub fn get_plmc_total_supply(&mut self) -> BalanceOf {
- self.execute(::NativeCurrency::total_issuance)
- }
-
- pub fn do_reserved_plmc_assertions(
- &mut self,
- correct_funds: Vec>,
- reserve_type: ::RuntimeHoldReason,
- ) {
- for UserToPLMCBalance { account, plmc_amount } in correct_funds {
- self.execute(|| {
- let reserved = ::NativeCurrency::balance_on_hold(&reserve_type, &account);
- assert_eq!(reserved, plmc_amount, "account has unexpected reserved plmc balance");
- });
- }
- }
-
- pub fn mint_plmc_to(&mut self, mapping: Vec>) {
- self.execute(|| {
- for UserToPLMCBalance { account, plmc_amount } in mapping {
- ::NativeCurrency::mint_into(&account, plmc_amount).expect("Minting should work");
- }
- });
- }
-
- pub fn mint_foreign_asset_to(&mut self, mapping: Vec>) {
- self.execute(|| {
- for UserToForeignAssets { account, asset_amount, asset_id } in mapping {
- ::FundingCurrency::mint_into(asset_id, &account, asset_amount)
- .expect("Minting should work");
- }
- });
- }
-
- pub fn current_block(&mut self) -> BlockNumberFor {
- self.execute(|| frame_system::Pallet::::block_number())
- }
-
- pub fn advance_time(&mut self, amount: BlockNumberFor) -> Result<(), DispatchError> {
- self.execute(|| {
- for _block in 0u32..amount.saturated_into() {
- let mut current_block = frame_system::Pallet::::block_number();
-
- >>::on_finalize(current_block);
- as OnFinalize>>::on_finalize(current_block);
-
- >>::on_idle(current_block, Weight::MAX);
- as OnIdle>>::on_idle(current_block, Weight::MAX);
-
- current_block += One::one();
- frame_system::Pallet::::set_block_number(current_block);
-
- as OnInitialize>>::on_initialize(current_block);
- >>::on_initialize(current_block);
- }
- Ok(())
- })
- }
-
- pub fn do_free_plmc_assertions(&mut self, correct_funds: Vec>) {
- for UserToPLMCBalance { account, plmc_amount } in correct_funds {
- self.execute(|| {
- let free = ::NativeCurrency::balance(&account);
- assert_eq!(free, plmc_amount, "account has unexpected free plmc balance");
- });
- }
- }
-
- pub fn do_free_foreign_asset_assertions(&mut self, correct_funds: Vec>) {
- for UserToForeignAssets { account, asset_amount, asset_id } in correct_funds {
- self.execute(|| {
- let real_amount = ::FundingCurrency::balance(asset_id, &account);
- assert_eq!(asset_amount, real_amount, "Wrong foreign asset balance expected for user {:?}", account);
- });
- }
- }
-
- pub fn do_bid_transferred_foreign_asset_assertions(
- &mut self,
- correct_funds: Vec>,
- project_id: ProjectId,
- ) {
- for UserToForeignAssets { account, asset_amount, .. } in correct_funds {
- self.execute(|| {
- // total amount of contributions for this user for this project stored in the mapping
- let contribution_total: ::Balance =
- Bids::::iter_prefix_values((project_id, account.clone()))
- .map(|c| c.funding_asset_amount_locked)
- .fold(Zero::zero(), |a, b| a + b);
- assert_eq!(
- contribution_total, asset_amount,
- "Wrong funding balance expected for stored auction info on user {:?}",
- account
- );
- });
- }
- }
-
- // Check if a Contribution storage item exists for the given funding asset transfer
- pub fn do_contribution_transferred_foreign_asset_assertions(
- &mut self,
- correct_funds: Vec>,
- project_id: ProjectId,
- ) {
- for UserToForeignAssets { account, asset_amount, .. } in correct_funds {
- self.execute(|| {
- Contributions::::iter_prefix_values((project_id, account.clone()))
- .find(|c| c.funding_asset_amount == asset_amount)
- .expect("Contribution not found in storage");
- });
- }
- }
-}
-
-// assertions
-impl<
- T: Config + pallet_balances::Config>,
- AllPalletsWithoutSystem: OnFinalize> + OnIdle> + OnInitialize>,
- RuntimeEvent: From> + TryInto> + Parameter + Member + IsType<::RuntimeEvent>,
- > Instantiator
-{
- pub fn test_ct_created_for(&mut self, project_id: ProjectId) {
- self.execute(|| {
- let metadata = ProjectsMetadata::::get(project_id).unwrap();
- assert_eq!(
- ::ContributionTokenCurrency::name(project_id),
- metadata.token_information.name.to_vec()
- );
- let escrow_account = Pallet::::fund_account_id(project_id);
-
- assert_eq!(::ContributionTokenCurrency::admin(project_id).unwrap(), escrow_account);
- });
- }
-
- pub fn test_ct_not_created_for(&mut self, project_id: ProjectId) {
- self.execute(|| {
- assert!(
- !::ContributionTokenCurrency::asset_exists(project_id),
- "Asset shouldn't exist, since funding failed"
- );
- });
- }
-
- pub fn creation_assertions(
- &mut self,
- project_id: ProjectId,
- expected_metadata: ProjectMetadataOf,
- creation_start_block: BlockNumberFor,
- ) {
- let metadata = self.get_project_metadata(project_id);
- let details = self.get_project_details(project_id);
- let expected_details = ProjectDetailsOf:: {
- issuer_account: self.get_issuer(project_id),
- issuer_did: generate_did_from_account(self.get_issuer(project_id)),
- is_frozen: false,
- weighted_average_price: None,
- status: ProjectStatus::Application,
- phase_transition_points: PhaseTransitionPoints {
- application: BlockNumberPair { start: Some(creation_start_block), end: None },
- ..Default::default()
- },
- fundraising_target: expected_metadata
- .minimum_price
- .checked_mul_int(expected_metadata.total_allocation_size)
- .unwrap(),
- remaining_contribution_tokens: expected_metadata.total_allocation_size,
- funding_amount_reached: BalanceOf::::zero(),
- evaluation_round_info: EvaluationRoundInfoOf:: {
- total_bonded_usd: Zero::zero(),
- total_bonded_plmc: Zero::zero(),
- evaluators_outcome: EvaluatorsOutcome::Unchanged,
- },
- funding_end_block: None,
- parachain_id: None,
- migration_readiness_check: None,
- hrmp_channel_status: HRMPChannelStatus {
- project_to_polimec: crate::ChannelStatus::Closed,
- polimec_to_project: crate::ChannelStatus::Closed,
- },
- };
- assert_eq!(metadata, expected_metadata);
- assert_eq!(details, expected_details);
- }
-
- pub fn evaluation_assertions(
- &mut self,
- project_id: ProjectId,
- expected_free_plmc_balances: Vec>,
- expected_reserved_plmc_balances: Vec>,
- total_plmc_supply: BalanceOf,
- ) {
- // just in case we forgot to merge accounts:
- let expected_free_plmc_balances =
- Self::generic_map_operation(vec![expected_free_plmc_balances], MergeOperation::Add);
- let expected_reserved_plmc_balances =
- Self::generic_map_operation(vec![expected_reserved_plmc_balances], MergeOperation::Add);
-
- let project_details = self.get_project_details(project_id);
-
- assert_eq!(project_details.status, ProjectStatus::EvaluationRound);
- assert_eq!(self.get_plmc_total_supply(), total_plmc_supply);
- self.do_free_plmc_assertions(expected_free_plmc_balances);
- self.do_reserved_plmc_assertions(expected_reserved_plmc_balances, HoldReason::Evaluation(project_id).into());
- }
-
- pub fn finalized_bids_assertions(
- &mut self,
- project_id: ProjectId,
- bid_expectations: Vec>,
- expected_ct_sold: BalanceOf,
- ) {
- let project_metadata = self.get_project_metadata(project_id);
- let project_details = self.get_project_details(project_id);
- let project_bids = self.execute(|| Bids::::iter_prefix_values((project_id,)).collect::>());
- assert!(project_details.weighted_average_price.is_some(), "Weighted average price should exist");
-
- for filter in bid_expectations {
- let _found_bid = project_bids.iter().find(|bid| filter.matches_bid(bid)).unwrap();
- }
-
- // Remaining CTs are updated
- assert_eq!(
- project_details.remaining_contribution_tokens,
- project_metadata.total_allocation_size - expected_ct_sold,
- "Remaining CTs are incorrect"
- );
- }
-}
-
-// calculations
-impl<
- T: Config + pallet_balances::Config>,
- AllPalletsWithoutSystem: OnFinalize> + OnIdle> + OnInitialize>,
- RuntimeEvent: From> + TryInto> + Parameter + Member + IsType<::RuntimeEvent>,
- > Instantiator
-{
- pub fn get_ed() -> BalanceOf {
- T::ExistentialDeposit::get()
- }
-
- pub fn get_ct_account_deposit() -> BalanceOf {
- ::ContributionTokenCurrency::deposit_required(One::one())
- }
-
- pub fn calculate_evaluation_plmc_spent(evaluations: Vec>) -> Vec> {
- let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap();
- let mut output = Vec::new();
- for eval in evaluations {
- let usd_bond = eval.usd_amount;
- let plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(usd_bond);
- output.push(UserToPLMCBalance::new(eval.account, plmc_bond));
- }
- output
- }
-
- pub fn get_actual_price_charged_for_bucketed_bids(
- bids: &Vec>,
- project_metadata: ProjectMetadataOf,
- maybe_bucket: Option>,
- ) -> Vec<(BidParams, PriceOf)> {
- let mut output = Vec::new();
- let mut bucket = if let Some(bucket) = maybe_bucket {
- bucket
- } else {
- Pallet::::create_bucket_from_metadata(&project_metadata).unwrap()
- };
- for bid in bids {
- let mut amount_to_bid = bid.amount;
- while !amount_to_bid.is_zero() {
- let bid_amount = if amount_to_bid <= bucket.amount_left { amount_to_bid } else { bucket.amount_left };
- output.push((
- BidParams {
- bidder: bid.bidder.clone(),
- amount: bid_amount,
- multiplier: bid.multiplier,
- asset: bid.asset,
- },
- bucket.current_price,
- ));
- bucket.update(bid_amount);
- amount_to_bid.saturating_reduce(bid_amount);
- }
- }
- output
- }
-
- pub fn calculate_auction_plmc_charged_with_given_price(
- bids: &Vec>,
- ct_price: PriceOf,
- ) -> Vec> {
- let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap();
- let mut output = Vec::new();
- for bid in bids {
- let usd_ticket_size = ct_price.saturating_mul_int(bid.amount);
- let usd_bond = bid.multiplier.calculate_bonding_requirement::(usd_ticket_size).unwrap();
- let plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(usd_bond);
- output.push(UserToPLMCBalance::new(bid.bidder.clone(), plmc_bond));
- }
- output
- }
-
- // Make sure you give it all the bids made for the project. It doesn't require a ct_price, since it will simulate the bucket prices itself
- pub fn calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket(
- bids: &Vec>,
- project_metadata: ProjectMetadataOf,
- maybe_bucket: Option>,
- ) -> Vec> {
- let mut output = Vec::new();
- let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap();
-
- for (bid, price) in Self::get_actual_price_charged_for_bucketed_bids(bids, project_metadata, maybe_bucket) {
- let usd_ticket_size = price.saturating_mul_int(bid.amount);
- let usd_bond = bid.multiplier.calculate_bonding_requirement::(usd_ticket_size).unwrap();
- let plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(usd_bond);
- output.push(UserToPLMCBalance::::new(bid.bidder.clone(), plmc_bond));
- }
-
- output.merge_accounts(MergeOperation::Add)
- }
-
- // WARNING: Only put bids that you are sure will be done before the random end of the closing auction
- pub fn calculate_auction_plmc_returned_from_all_bids_made(
- // bids in the order they were made
- bids: &Vec>,
- project_metadata: ProjectMetadataOf,
- weighted_average_price: PriceOf,
- ) -> Vec> {
- let mut output = Vec::new();
- let charged_bids = Self::get_actual_price_charged_for_bucketed_bids(bids, project_metadata.clone(), None);
- let grouped_by_price_bids = charged_bids.clone().into_iter().group_by(|&(_, price)| price);
- let mut grouped_by_price_bids: Vec<(PriceOf, Vec>)> = grouped_by_price_bids
- .into_iter()
- .map(|(key, group)| (key, group.map(|(bid, _price_)| bid).collect()))
- .collect();
- grouped_by_price_bids.reverse();
-
- let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap();
- let mut remaining_cts =
- project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size;
-
- for (price_charged, bids) in grouped_by_price_bids {
- for bid in bids {
- let charged_usd_ticket_size = price_charged.saturating_mul_int(bid.amount);
- let charged_usd_bond =
- bid.multiplier.calculate_bonding_requirement::(charged_usd_ticket_size).unwrap();
- let charged_plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(charged_usd_bond);
-
- if remaining_cts <= Zero::zero() {
- output.push(UserToPLMCBalance::new(bid.bidder, charged_plmc_bond));
- continue
- }
-
- let bought_cts = if remaining_cts < bid.amount { remaining_cts } else { bid.amount };
- remaining_cts = remaining_cts.saturating_sub(bought_cts);
-
- let final_price =
- if weighted_average_price > price_charged { price_charged } else { weighted_average_price };
-
- let actual_usd_ticket_size = final_price.saturating_mul_int(bought_cts);
- let actual_usd_bond =
- bid.multiplier.calculate_bonding_requirement::(actual_usd_ticket_size).unwrap();
- let actual_plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(actual_usd_bond);
-
- let returned_plmc_bond = charged_plmc_bond - actual_plmc_bond;
-
- output.push(UserToPLMCBalance::::new(bid.bidder, returned_plmc_bond));
- }
- }
-
- output.merge_accounts(MergeOperation::Add)
- }
-
- pub fn calculate_auction_plmc_spent_post_wap(
- bids: &Vec>,
- project_metadata: ProjectMetadataOf,
- weighted_average_price: PriceOf,
- ) -> Vec> {
- let plmc_charged = Self::calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket(
- bids,
- project_metadata.clone(),
- None,
- );
- let plmc_returned = Self::calculate_auction_plmc_returned_from_all_bids_made(
- bids,
- project_metadata.clone(),
- weighted_average_price,
- );
-
- plmc_charged.subtract_accounts(plmc_returned)
- }
-
- pub fn calculate_auction_funding_asset_charged_with_given_price(
- bids: &Vec>,
- ct_price: PriceOf,
- ) -> Vec> {
- let mut output = Vec::new();
- for bid in bids {
- let asset_price = T::PriceProvider::get_price(bid.asset.to_assethub_id()).unwrap();
- let usd_ticket_size = ct_price.saturating_mul_int(bid.amount);
- let funding_asset_spent = asset_price.reciprocal().unwrap().saturating_mul_int(usd_ticket_size);
- output.push(UserToForeignAssets::new(bid.bidder.clone(), funding_asset_spent, bid.asset.to_assethub_id()));
- }
- output
- }
-
- // Make sure you give it all the bids made for the project. It doesn't require a ct_price, since it will simulate the bucket prices itself
- pub fn calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket(
- bids: &Vec>,
- project_metadata: ProjectMetadataOf,
- maybe_bucket: Option>,
- ) -> Vec> {
- let mut output = Vec::new();
-
- for (bid, price) in Self::get_actual_price_charged_for_bucketed_bids(bids, project_metadata, maybe_bucket) {
- let asset_price = T::PriceProvider::get_price(bid.asset.to_assethub_id()).unwrap();
- let usd_ticket_size = price.saturating_mul_int(bid.amount);
- let funding_asset_spent = asset_price.reciprocal().unwrap().saturating_mul_int(usd_ticket_size);
- output.push(UserToForeignAssets::::new(
- bid.bidder.clone(),
- funding_asset_spent,
- bid.asset.to_assethub_id(),
- ));
- }
-
- output.merge_accounts(MergeOperation::Add)
- }
-
- // WARNING: Only put bids that you are sure will be done before the random end of the closing auction
- pub fn calculate_auction_funding_asset_returned_from_all_bids_made(
- // bids in the order they were made
- bids: &Vec>,
- project_metadata: ProjectMetadataOf,
- weighted_average_price: PriceOf,
- ) -> Vec> {
- let mut output = Vec::new();
- let charged_bids = Self::get_actual_price_charged_for_bucketed_bids(bids, project_metadata.clone(), None);
- let grouped_by_price_bids = charged_bids.clone().into_iter().group_by(|&(_, price)| price);
- let mut grouped_by_price_bids: Vec<(PriceOf, Vec>)> = grouped_by_price_bids
- .into_iter()
- .map(|(key, group)| (key, group.map(|(bid, _price)| bid).collect()))
- .collect();
- grouped_by_price_bids.reverse();
-
- let mut remaining_cts =
- project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size;
-
- for (price_charged, bids) in grouped_by_price_bids {
- for bid in bids {
- let funding_asset_price = T::PriceProvider::get_price(bid.asset.to_assethub_id()).unwrap();
-
- let charged_usd_ticket_size = price_charged.saturating_mul_int(bid.amount);
- let charged_usd_bond =
- bid.multiplier.calculate_bonding_requirement::(charged_usd_ticket_size).unwrap();
- let charged_funding_asset =
- funding_asset_price.reciprocal().unwrap().saturating_mul_int(charged_usd_bond);
-
- if remaining_cts <= Zero::zero() {
- output.push(UserToForeignAssets::new(
- bid.bidder,
- charged_funding_asset,
- bid.asset.to_assethub_id(),
- ));
- continue
- }
-
- let bought_cts = if remaining_cts < bid.amount { remaining_cts } else { bid.amount };
- remaining_cts = remaining_cts.saturating_sub(bought_cts);
-
- let final_price =
- if weighted_average_price > price_charged { price_charged } else { weighted_average_price };
-
- let actual_usd_ticket_size = final_price.saturating_mul_int(bought_cts);
- let actual_usd_bond =
- bid.multiplier.calculate_bonding_requirement::(actual_usd_ticket_size).unwrap();
- let actual_funding_asset_spent =
- funding_asset_price.reciprocal().unwrap().saturating_mul_int(actual_usd_bond);
-
- let returned_foreign_asset = charged_funding_asset - actual_funding_asset_spent;
-
- output.push(UserToForeignAssets::::new(
- bid.bidder,
- returned_foreign_asset,
- bid.asset.to_assethub_id(),
- ));
- }
- }
-
- output.merge_accounts(MergeOperation::Add)
- }
-
- pub fn calculate_auction_funding_asset_spent_post_wap(
- bids: &Vec>,
- project_metadata: ProjectMetadataOf,
- weighted_average_price: PriceOf,
- ) -> Vec> {
- let funding_asset_charged = Self::calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket(
- bids,
- project_metadata.clone(),
- None,
- );
- let funding_asset_returned = Self::calculate_auction_funding_asset_returned_from_all_bids_made(
- bids,
- project_metadata.clone(),
- weighted_average_price,
- );
-
- funding_asset_charged.subtract_accounts(funding_asset_returned)
- }
-
- /// Filters the bids that would be rejected after the auction ends.
- pub fn filter_bids_after_auction(bids: Vec>, total_cts: BalanceOf) -> Vec> {
- let mut filtered_bids: Vec> = Vec::new();
- let sorted_bids = bids;
- let mut total_cts_left = total_cts;
- for bid in sorted_bids {
- if total_cts_left >= bid.amount {
- total_cts_left.saturating_reduce(bid.amount);
- filtered_bids.push(bid);
- } else if !total_cts_left.is_zero() {
- filtered_bids.push(BidParams {
- bidder: bid.bidder.clone(),
- amount: total_cts_left,
- multiplier: bid.multiplier,
- asset: bid.asset,
- });
- total_cts_left = Zero::zero();
- }
- }
- filtered_bids
- }
-
- pub fn calculate_contributed_plmc_spent(
- contributions: Vec>,
- token_usd_price: PriceOf,
- ) -> Vec> {
- let plmc_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).unwrap();
- let mut output = Vec::new();
- for cont in contributions {
- let usd_ticket_size = token_usd_price.saturating_mul_int(cont.amount);
- let usd_bond = cont.multiplier.calculate_bonding_requirement::(usd_ticket_size).unwrap();
- let plmc_bond = plmc_price.reciprocal().unwrap().saturating_mul_int(usd_bond);
- output.push(UserToPLMCBalance::new(cont.contributor, plmc_bond));
- }
- output
- }
-
- pub fn calculate_total_plmc_locked_from_evaluations_and_remainder_contributions(
- evaluations: Vec>,
- contributions: Vec>,
- price: PriceOf,
- slashed: bool,
- ) -> Vec> {
- let evaluation_locked_plmc_amounts = Self::calculate_evaluation_plmc_spent(evaluations);
- // how much new plmc would be locked without considering evaluation bonds
- let theoretical_contribution_locked_plmc_amounts = Self::calculate_contributed_plmc_spent(contributions, price);
-
- let slash_percentage = ::EvaluatorSlash::get();
- let slashable_min_deposits = evaluation_locked_plmc_amounts
- .iter()
- .map(|UserToPLMCBalance { account, plmc_amount }| UserToPLMCBalance {
- account: account.clone(),
- plmc_amount: slash_percentage * *plmc_amount,
- })
- .collect::>();
- let available_evaluation_locked_plmc_for_lock_transfer = Self::generic_map_operation(
- vec![evaluation_locked_plmc_amounts.clone(), slashable_min_deposits.clone()],
- MergeOperation::Subtract,
- );
-
- // how much new plmc was actually locked, considering already evaluation bonds used
- // first.
- let actual_contribution_locked_plmc_amounts = Self::generic_map_operation(
- vec![theoretical_contribution_locked_plmc_amounts, available_evaluation_locked_plmc_for_lock_transfer],
- MergeOperation::Subtract,
- );
- let mut result = Self::generic_map_operation(
- vec![evaluation_locked_plmc_amounts, actual_contribution_locked_plmc_amounts],
- MergeOperation::Add,
- );
-
- if slashed {
- result = Self::generic_map_operation(vec![result, slashable_min_deposits], MergeOperation::Subtract);
- }
-
- result
- }
-
- pub fn calculate_contributed_funding_asset_spent(
- contributions: Vec>,
- token_usd_price: PriceOf,
- ) -> Vec> {
- let mut output = Vec::new();
- for cont in contributions {
- let asset_price = T::PriceProvider::get_price(cont.asset.to_assethub_id()).unwrap();
- let usd_ticket_size = token_usd_price.saturating_mul_int(cont.amount);
- let funding_asset_spent = asset_price.reciprocal().unwrap().saturating_mul_int(usd_ticket_size);
- output.push(UserToForeignAssets::new(cont.contributor, funding_asset_spent, cont.asset.to_assethub_id()));
- }
- output
- }
-
- pub fn generic_map_merge_reduce(
- 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()
- }
-
- /// Merge the given mappings into one mapping, where the values are merged using the given
- /// merge operation.
- ///
- /// In case of the `Add` operation, all values are Unioned, and duplicate accounts are
- /// added together.
- /// In case of the `Subtract` operation, all values of the first mapping are subtracted by
- /// the values of the other mappings. Accounts in the other mappings that are not present
- /// in the first mapping are ignored.
- ///
- /// # Pseudocode Example
- /// List1: [(A, 10), (B, 5), (C, 5)]
- /// List2: [(A, 5), (B, 5), (D, 5)]
- ///
- /// Add: [(A, 15), (B, 10), (C, 5), (D, 5)]
- /// Subtract: [(A, 5), (B, 0), (C, 5)]
- pub fn generic_map_operation<
- N: AccountMerge + Extend<::Inner> + IntoIterator- ::Inner>,
- >(
- mut mappings: Vec,
- ops: MergeOperation,
- ) -> N {
- let mut output = mappings.swap_remove(0);
- output = output.merge_accounts(MergeOperation::Add);
- for map in mappings {
- match ops {
- MergeOperation::Add => output.extend(map),
- MergeOperation::Subtract => output = output.subtract_accounts(map),
- }
- }
- output.merge_accounts(ops)
- }
-
- pub fn sum_balance_mappings(mut mappings: Vec>>) -> BalanceOf {
- let mut output = mappings
- .swap_remove(0)
- .into_iter()
- .map(|user_to_plmc| user_to_plmc.plmc_amount)
- .fold(Zero::zero(), |a, b| a + b);
- for map in mappings {
- output += map.into_iter().map(|user_to_plmc| user_to_plmc.plmc_amount).fold(Zero::zero(), |a, b| a + b);
- }
- output
- }
-
- pub fn sum_foreign_mappings(mut mappings: Vec>>) -> BalanceOf {
- let mut output = mappings
- .swap_remove(0)
- .into_iter()
- .map(|user_to_asset| user_to_asset.asset_amount)
- .fold(Zero::zero(), |a, b| a + b);
- for map in mappings {
- output += map.into_iter().map(|user_to_asset| user_to_asset.asset_amount).fold(Zero::zero(), |a, b| a + b);
- }
- output
- }
-
- pub fn generate_successful_evaluations(
- project_metadata: ProjectMetadataOf,
- evaluators: Vec>,
- weights: Vec,
- ) -> Vec> {
- let funding_target = project_metadata.minimum_price.saturating_mul_int(project_metadata.total_allocation_size);
- let evaluation_success_threshold = ::EvaluationSuccessThreshold::get(); // if we use just the threshold, then for big usd targets we lose the evaluation due to PLMC conversion errors in `evaluation_end`
- let usd_threshold = evaluation_success_threshold * funding_target * 2u32.into();
-
- zip(evaluators, weights)
- .map(|(evaluator, weight)| {
- let ticket_size = Percent::from_percent(weight) * usd_threshold;
- (evaluator, ticket_size).into()
- })
- .collect()
- }
-
- pub fn generate_bids_from_total_usd(
- usd_amount: BalanceOf,
- min_price: PriceOf,
- weights: Vec,
- bidders: Vec>,
- multipliers: Vec,
- ) -> Vec> {
- assert_eq!(weights.len(), bidders.len(), "Should have enough weights for all the bidders");
-
- zip(zip(weights, bidders), multipliers)
- .map(|((weight, bidder), multiplier)| {
- let ticket_size = Percent::from_percent(weight) * usd_amount;
- let token_amount = min_price.reciprocal().unwrap().saturating_mul_int(ticket_size);
-
- BidParams::new(bidder, token_amount, multiplier, AcceptedFundingAsset::USDT)
- })
- .collect()
- }
-
- pub fn generate_bids_from_total_ct_percent(
- project_metadata: ProjectMetadataOf,
- percent_funding: u8,
- weights: Vec,
- bidders: Vec>,
- multipliers: Vec,
- ) -> Vec> {
- let total_allocation_size = project_metadata.total_allocation_size;
- let total_ct_bid = Percent::from_percent(percent_funding) * total_allocation_size;
-
- assert_eq!(weights.len(), bidders.len(), "Should have enough weights for all the bidders");
-
- zip(zip(weights, bidders), multipliers)
- .map(|((weight, bidder), multiplier)| {
- let token_amount = Percent::from_percent(weight) * total_ct_bid;
- BidParams::new(bidder, token_amount, multiplier, AcceptedFundingAsset::USDT)
- })
- .collect()
- }
-
- pub fn generate_contributions_from_total_usd(
- usd_amount: BalanceOf,
- final_price: PriceOf,
- weights: Vec,
- contributors: Vec>,
- multipliers: Vec,
- ) -> Vec> {
- zip(zip(weights, contributors), multipliers)
- .map(|((weight, bidder), multiplier)| {
- let ticket_size = Percent::from_percent(weight) * usd_amount;
- let token_amount = final_price.reciprocal().unwrap().saturating_mul_int(ticket_size);
-
- ContributionParams::new(bidder, token_amount, multiplier, AcceptedFundingAsset::USDT)
- })
- .collect()
- }
-
- pub fn generate_contributions_from_total_ct_percent(
- project_metadata: ProjectMetadataOf,
- percent_funding: u8,
- weights: Vec,
- contributors: Vec>,
- multipliers: Vec,
- ) -> Vec> {
- let total_allocation_size = project_metadata.total_allocation_size;
- let total_ct_bought = Percent::from_percent(percent_funding) * total_allocation_size;
-
- assert_eq!(weights.len(), contributors.len(), "Should have enough weights for all the bidders");
-
- zip(zip(weights, contributors), multipliers)
- .map(|((weight, contributor), multiplier)| {
- let token_amount = Percent::from_percent(weight) * total_ct_bought;
- ContributionParams::new(contributor, token_amount, multiplier, AcceptedFundingAsset::USDT)
- })
- .collect()
- }
-
- pub fn slash_evaluator_balances(mut balances: Vec>) -> Vec> {
- let slash_percentage = ::EvaluatorSlash::get();
- for UserToPLMCBalance { account: _acc, plmc_amount: balance } in balances.iter_mut() {
- *balance -= slash_percentage * *balance;
- }
- balances
- }
-
- pub fn calculate_total_reward_for_evaluation(
- evaluation: EvaluationInfoOf,
- reward_info: RewardInfoOf,
- ) -> BalanceOf {
- let early_reward_weight =
- Perquintill::from_rational(evaluation.early_usd_amount, reward_info.early_evaluator_total_bonded_usd);
- let normal_reward_weight = Perquintill::from_rational(
- evaluation.late_usd_amount.saturating_add(evaluation.early_usd_amount),
- reward_info.normal_evaluator_total_bonded_usd,
- );
- let early_evaluators_rewards = early_reward_weight * reward_info.early_evaluator_reward_pot;
- let normal_evaluators_rewards = normal_reward_weight * reward_info.normal_evaluator_reward_pot;
-
- early_evaluators_rewards.saturating_add(normal_evaluators_rewards)
- }
-}
-
-// project chain interactions
-impl<
- T: Config + pallet_balances::Config>,
- AllPalletsWithoutSystem: OnFinalize> + OnIdle> + OnInitialize>,
- RuntimeEvent: From> + TryInto> + Parameter + Member + IsType<::RuntimeEvent>,
- > Instantiator
-{
- pub fn get_issuer(&mut self, project_id: ProjectId) -> AccountIdOf {
- self.execute(|| ProjectsDetails::::get(project_id).unwrap().issuer_account)
- }
-
- pub fn get_project_metadata(&mut self, project_id: ProjectId) -> ProjectMetadataOf {
- self.execute(|| ProjectsMetadata::::get(project_id).expect("Project metadata exists"))
- }
-
- pub fn get_project_details(&mut self, project_id: ProjectId) -> ProjectDetailsOf {
- self.execute(|| ProjectsDetails::::get(project_id).expect("Project details exists"))
- }
-
- pub fn get_update_block(&mut self, project_id: ProjectId, update_type: &UpdateType) -> Option> {
- self.execute(|| {
- ProjectsToUpdate::::iter().find_map(|(block, update_tup)| {
- if project_id == update_tup.0 && update_type == &update_tup.1 {
- Some(block)
- } else {
- None
- }
- })
- })
- }
-
- pub fn create_new_project(&mut self, project_metadata: ProjectMetadataOf, issuer: AccountIdOf) -> ProjectId {
- let now = self.current_block();
- // one ED for the issuer, one ED for the escrow account
- self.mint_plmc_to(vec![UserToPLMCBalance::new(issuer.clone(), Self::get_ed() * 2u64.into())]);
-
- self.execute(|| {
- crate::Pallet::::do_create_project(
- &issuer,
- project_metadata.clone(),
- generate_did_from_account(issuer.clone()),
- )
- .unwrap();
- let last_project_metadata = ProjectsMetadata::::iter().last().unwrap();
- log::trace!("Last project metadata: {:?}", last_project_metadata);
- });
-
- let created_project_id = self.execute(|| NextProjectId::::get().saturating_sub(One::one()));
- self.creation_assertions(created_project_id, project_metadata, now);
- created_project_id
- }
-
- pub fn start_evaluation(&mut self, project_id: ProjectId, caller: AccountIdOf