Skip to content

Commit

Permalink
new funding runtime apis
Browse files Browse the repository at this point in the history
  • Loading branch information
JuaniRios committed Oct 14, 2024
1 parent 1dccca3 commit 5780289
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 23 deletions.
2 changes: 2 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ std = [
"system-parachains-constants/std",
"xcm-builder/std",
"xcm-executor/std",
"xcm-fee-payment-runtime-api/std",
"xcm/std",
]
development-settings = [ "polimec-runtime/development-settings" ]
Expand Down Expand Up @@ -196,5 +197,6 @@ runtime-benchmarks = [
"sp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
"xcm-fee-payment-runtime-api/runtime-benchmarks",
]

3 changes: 0 additions & 3 deletions integration-tests/src/tests/runtime_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,12 @@ mod xcm_payment_api {
VersionedAssetId::V4(AssetId(Location { parents: 0, interior: Here })),
)
.unwrap();
dbg!(plmc_fee);

let dot_fee = PolimecRuntime::query_weight_to_asset_fee(
compute_weight,
VersionedAssetId::V4(AssetId(Location { parents: 1, interior: Here })),
)
.unwrap();
dbg!(dot_fee);

let usdt_fee = PolimecRuntime::query_weight_to_asset_fee(
compute_weight,
Expand All @@ -64,7 +62,6 @@ mod xcm_payment_api {
})),
)
.unwrap();
dbg!(usdt_fee);

// PLMC and dot have the same decimals, so a simple conversion is enough
assert_eq!(dot_fee, plmc_fee / 20);
Expand Down
7 changes: 7 additions & 0 deletions pallets/funding/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,13 @@ sp_api::mock_impl_runtime_apis! {
PolimecFunding::get_next_vesting_schedule_merge_candidates(account, hold_reason, end_max_delta)
}

fn calculate_otm_fee(funding_asset: AcceptedFundingAsset, funding_asset_amount: Balance) -> Option<Balance> {
PolimecFunding::calculate_otm_fee(funding_asset, funding_asset_amount)
}
fn get_funding_asset_min_max_amounts(project_id: ProjectId, did: Did, funding_asset: AcceptedFundingAsset, investor_type: InvestorType) -> Option<(Balance, Balance)> {
PolimecFunding::get_funding_asset_min_max_amounts(project_id, did, funding_asset, investor_type)
}


}
}
105 changes: 103 additions & 2 deletions pallets/funding/src/runtime_api.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::traits::BondingRequirementCalculation;
#[allow(clippy::wildcard_imports)]
use crate::*;
use alloc::collections::BTreeMap;
use frame_support::traits::fungibles::{metadata::Inspect as MetadataInspect, Inspect, InspectEnumerable};
use itertools::Itertools;
use parity_scale_codec::{Decode, Encode};
use polimec_common::{ProvideAssetPrice, USD_DECIMALS};
use polimec_common::{credentials::InvestorType, ProvideAssetPrice, USD_DECIMALS};
use scale_info::TypeInfo;
use sp_runtime::traits::Zero;
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)]
Expand Down Expand Up @@ -52,7 +53,7 @@ sp_api::decl_runtime_apis! {
fn projects_by_did(did: Did) -> Vec<ProjectId>;
}

#[api_version(2)]
#[api_version(3)]
pub trait ExtrinsicHelpers<T: Config> {
/// Get the current price of a contribution token (either current bucket in the auction, or WAP in contribution phase),
/// and calculate the amount of tokens that can be bought with the given amount USDT/USDC/DOT.
Expand All @@ -61,6 +62,12 @@ sp_api::decl_runtime_apis! {
/// Get the indexes of vesting schedules that are good candidates to be merged.
/// Schedules that have not yet started are de-facto bad candidates.
fn get_next_vesting_schedule_merge_candidates(account_id: AccountIdOf<T>, hold_reason: <T as Config>::RuntimeHoldReason, end_max_delta: Balance) -> Option<(u32, u32)>;

/// Calculate the OTM fee for a project, using a given asset and amount.
fn calculate_otm_fee(funding_asset: AcceptedFundingAsset, funding_asset_amount: Balance) -> Option<Balance>;

/// Gets the minimum and maximum amount of FundingAsset a user can input in the UI.
fn get_funding_asset_min_max_amounts(project_id: ProjectId, did: Did, funding_asset: AcceptedFundingAsset, investor_type: InvestorType) -> Option<(Balance, Balance)>;
}
}

Expand Down Expand Up @@ -207,6 +214,100 @@ impl<T: Config> Pallet<T> {
None
}

pub fn calculate_otm_fee(funding_asset: AcceptedFundingAsset, funding_asset_amount: Balance) -> Option<Balance> {
let plmc_price = <PriceProviderOf<T>>::get_decimals_aware_price(PLMC_FOREIGN_ID, USD_DECIMALS, PLMC_DECIMALS)
.expect("Price not found");
let funding_asset_id = funding_asset.id();
let funding_asset_decimals = T::FundingCurrency::decimals(funding_asset_id);
let funding_asset_usd_price =
<PriceProviderOf<T>>::get_decimals_aware_price(funding_asset_id, USD_DECIMALS, funding_asset_decimals)
.expect("Price not found");
let usd_amount = funding_asset_usd_price.saturating_mul_int(funding_asset_amount);
let otm_multiplier: MultiplierOf<T> = ParticipationMode::OTM.multiplier().try_into().ok()?;
let required_usd_bond = otm_multiplier.calculate_usd_bonding_requirement::<T>(usd_amount)?;
let plmc_bond = plmc_price.reciprocal()?.saturating_mul_int(required_usd_bond);
pallet_proxy_bonding::Pallet::<T>::calculate_fee(plmc_bond, funding_asset_id).ok()
}

pub fn get_funding_asset_min_max_amounts(
project_id: ProjectId,
did: Did,
funding_asset: AcceptedFundingAsset,
investor_type: InvestorType,
) -> Option<(Balance, Balance)> {
let project_details = ProjectsDetails::<T>::get(project_id)?;
let project_metadata = ProjectsMetadata::<T>::get(project_id)?;
let funding_asset_id = funding_asset.id();
let funding_asset_price = <PriceProviderOf<T>>::get_decimals_aware_price(
funding_asset_id,
USD_DECIMALS,
T::FundingCurrency::decimals(funding_asset_id),
)?;

let (min_usd_ticket, maybe_max_usd_ticket, already_spent_usd, total_cts_usd_amount) =
match project_details.status {
ProjectStatus::AuctionRound => {
let ticket_sizes = match investor_type {
InvestorType::Institutional => project_metadata.bidding_ticket_sizes.institutional,
InvestorType::Professional => project_metadata.bidding_ticket_sizes.professional,
_ => return None,
};
let already_spent_usd = AuctionBoughtUSD::<T>::get((project_id, did));
let mut max_contribution_tokens =
project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size;

let mut total_cts_usd_amount = 0;

let mut current_bucket = Buckets::<T>::get(project_id)?;
while max_contribution_tokens > 0u128 {
let bucket_price = current_bucket.current_price;
let ct_to_buy = max_contribution_tokens.min(current_bucket.amount_left);
let usd_spent = bucket_price.saturating_mul_int(ct_to_buy);

max_contribution_tokens -= ct_to_buy;
total_cts_usd_amount += usd_spent;
current_bucket.update(ct_to_buy);
}

(
ticket_sizes.usd_minimum_per_participation,
ticket_sizes.usd_maximum_per_did,
already_spent_usd,
total_cts_usd_amount,
)
},
ProjectStatus::CommunityRound(..) => {
let ticket_sizes = match investor_type {
InvestorType::Institutional => project_metadata.contributing_ticket_sizes.institutional,
InvestorType::Professional => project_metadata.contributing_ticket_sizes.professional,
InvestorType::Retail => project_metadata.contributing_ticket_sizes.retail,
};
let already_spent_usd = ContributionBoughtUSD::<T>::get((project_id, did));
let max_contribution_tokens = project_details.remaining_contribution_tokens;
let price = project_details.weighted_average_price?;
let total_cts_usd_amount = price.saturating_mul_int(max_contribution_tokens);
(
ticket_sizes.usd_minimum_per_participation,
ticket_sizes.usd_maximum_per_did,
already_spent_usd,
total_cts_usd_amount,
)
},
_ => return None,
};

let max_usd_ticket = if let Some(issuer_set_max_usd_ticket) = maybe_max_usd_ticket {
total_cts_usd_amount.min(issuer_set_max_usd_ticket.saturating_sub(already_spent_usd))
} else {
total_cts_usd_amount
};

let funding_asset_min_ticket = funding_asset_price.reciprocal()?.saturating_mul_int(min_usd_ticket);
let funding_asset_max_ticket = funding_asset_price.reciprocal()?.saturating_mul_int(max_usd_ticket);

Some((funding_asset_min_ticket, funding_asset_max_ticket))
}

pub fn all_project_participations_by_did(project_id: ProjectId, did: Did) -> Vec<ProjectParticipationIds<T>> {
let evaluations = Evaluations::<T>::iter_prefix((project_id,))
.filter(|((_account_id, _evaluation_id), evaluation)| evaluation.did == did)
Expand Down
Loading

0 comments on commit 5780289

Please sign in to comment.