Skip to content

Commit

Permalink
👾 New Funding Runtime API (#421)
Browse files Browse the repository at this point in the history
## What?
New API to get the CTs and OTM fee (in the funding asset) by inputting the project id, funding asset, and funding asset amount

## Why?
Needed for the UI
  • Loading branch information
JuaniRios authored Oct 16, 2024
1 parent 92b3b1e commit ee93b3a
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 17 deletions.
10 changes: 8 additions & 2 deletions pallets/funding/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,16 +571,22 @@ sp_api::mock_impl_runtime_apis! {
}

impl ExtrinsicHelpers<Block, TestRuntime> for TestRuntime {
fn funding_asset_to_ct_amount(project_id: ProjectId, asset: AcceptedFundingAsset, asset_amount: Balance) -> Balance {
PolimecFunding::funding_asset_to_ct_amount(project_id, asset, asset_amount)
fn funding_asset_to_ct_amount_classic(project_id: ProjectId, funding_asset: AcceptedFundingAsset, funding_asset_amount: Balance) -> Balance {
PolimecFunding::funding_asset_to_ct_amount_classic(project_id, funding_asset, funding_asset_amount)
}

fn funding_asset_to_ct_amount_otm(project_id: ProjectId, funding_asset: AcceptedFundingAsset, funding_asset_amount: Balance) -> (Balance, Balance) {
PolimecFunding::funding_asset_to_ct_amount_otm(project_id, funding_asset, funding_asset_amount)
}

fn get_next_vesting_schedule_merge_candidates(account: AccountId, hold_reason: RuntimeHoldReason, end_max_delta: Balance) -> Option<(u32, u32)> {
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)
}
Expand Down
60 changes: 57 additions & 3 deletions pallets/funding/src/runtime_api.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use crate::traits::BondingRequirementCalculation;
#[allow(clippy::wildcard_imports)]
use crate::*;
use crate::{traits::BondingRequirementCalculation, HoldReason::Participation};
use alloc::collections::BTreeMap;
use frame_support::traits::fungibles::{Inspect, InspectEnumerable};
use itertools::Itertools;
use parity_scale_codec::{Decode, Encode};
use polimec_common::{credentials::InvestorType, ProvideAssetPrice, USD_DECIMALS};
use scale_info::TypeInfo;
use sp_arithmetic::Perquintill;
use sp_core::Get;
use sp_runtime::traits::Zero;

#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)]
pub struct ProjectParticipationIds<T: Config> {
account: AccountIdOf<T>,
Expand Down Expand Up @@ -57,7 +60,10 @@ sp_api::decl_runtime_apis! {
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.
fn funding_asset_to_ct_amount(project_id: ProjectId, asset: AcceptedFundingAsset, asset_amount: Balance) -> Balance;
fn funding_asset_to_ct_amount_classic(project_id: ProjectId, funding_asset: AcceptedFundingAsset, funding_asset_amount: Balance) -> Balance;

/// Calculate how many CTs and what the OTM fee is for a given project and funding asset amount.
fn funding_asset_to_ct_amount_otm(project_id: ProjectId, funding_asset: AcceptedFundingAsset, funding_asset_amount: Balance) -> (Balance, Balance);

/// Get the indexes of vesting schedules that are good candidates to be merged.
/// Schedules that have not yet started are de-facto bad candidates.
Expand All @@ -68,6 +74,7 @@ sp_api::decl_runtime_apis! {

/// 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 @@ -138,7 +145,7 @@ impl<T: Config> Pallet<T> {
.collect_vec()
}

pub fn funding_asset_to_ct_amount(
pub fn funding_asset_to_ct_amount_classic(
project_id: ProjectId,
asset: AcceptedFundingAsset,
asset_amount: Balance,
Expand Down Expand Up @@ -176,6 +183,53 @@ impl<T: Config> Pallet<T> {
ct_amount
}

pub fn funding_asset_to_ct_amount_otm(
project_id: ProjectId,
funding_asset: AcceptedFundingAsset,
total_funding_asset_amount: Balance,
) -> (Balance, Balance) {
let project_details = ProjectsDetails::<T>::get(project_id).expect("Project not found");
let funding_asset_usd_price =
Pallet::<T>::get_decimals_aware_funding_asset_price(&funding_asset).expect("Price not found");
let otm_multiplier = ParticipationMode::OTM.multiplier();
let otm_fee_percentage = <T as pallet_proxy_bonding::Config>::FeePercentage::get() / otm_multiplier;

let divisor = FixedU128::from_perbill(otm_fee_percentage) + FixedU128::one();
let participating_funding_asset_amount =
divisor.reciprocal().unwrap().saturating_mul_int(total_funding_asset_amount);
let fee_funding_asset_amount = total_funding_asset_amount.saturating_sub(participating_funding_asset_amount);

let participating_usd_ticket_size =
funding_asset_usd_price.saturating_mul_int(participating_funding_asset_amount);

let mut ct_amount = Zero::zero();

// Contribution phase
if let Some(wap) = project_details.weighted_average_price {
ct_amount = wap.reciprocal().expect("Bad math").saturating_mul_int(participating_usd_ticket_size);
}
// Auction phase, we need to consider multiple buckets
else {
let mut usd_to_spend = participating_usd_ticket_size;
let mut current_bucket = Buckets::<T>::get(project_id).expect("Bucket not found");
while usd_to_spend > Zero::zero() {
let bucket_price = current_bucket.current_price;

let ct_to_buy = bucket_price.reciprocal().expect("Bad math").saturating_mul_int(usd_to_spend);
let ct_to_buy = ct_to_buy.min(current_bucket.amount_left);

ct_amount = ct_amount.saturating_add(ct_to_buy);
// if usd spent is 0, we will have an infinite loop
let usd_spent = bucket_price.saturating_mul_int(ct_to_buy).max(One::one());
usd_to_spend = usd_to_spend.saturating_sub(usd_spent);

current_bucket.update(ct_to_buy)
}
}

(ct_amount, fee_funding_asset_amount)
}

pub fn get_next_vesting_schedule_merge_candidates(
account_id: AccountIdOf<T>,
hold_reason: <T as Config>::RuntimeHoldReason,
Expand Down
4 changes: 2 additions & 2 deletions pallets/funding/src/tests/3_auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ mod bid_extrinsic {
inst.mint_funding_asset_to(vec![required_usdt.clone()]);

let ct_participation = inst.execute(|| {
<Pallet<TestRuntime>>::funding_asset_to_ct_amount(
<Pallet<TestRuntime>>::funding_asset_to_ct_amount_classic(
project_id,
AcceptedFundingAsset::USDT,
USDT_PARTICIPATION,
Expand Down Expand Up @@ -1057,7 +1057,7 @@ mod bid_extrinsic {
inst.mint_funding_asset_to(vec![required_usdt.clone()]);

let ct_participation = inst.execute(|| {
<Pallet<TestRuntime>>::funding_asset_to_ct_amount(
<Pallet<TestRuntime>>::funding_asset_to_ct_amount_classic(
project_id,
AcceptedFundingAsset::USDT,
USDT_PARTICIPATION,
Expand Down
4 changes: 2 additions & 2 deletions pallets/funding/src/tests/4_contribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,7 @@ mod contribute_extrinsic {
inst.mint_funding_asset_to(vec![required_usdt.clone()]);

let ct_participation = inst.execute(|| {
<Pallet<TestRuntime>>::funding_asset_to_ct_amount(
<Pallet<TestRuntime>>::funding_asset_to_ct_amount_classic(
project_id,
AcceptedFundingAsset::USDT,
USDT_PARTICIPATION,
Expand Down Expand Up @@ -1221,7 +1221,7 @@ mod contribute_extrinsic {
inst.mint_funding_asset_to(vec![required_usdt.clone()]);

let ct_participation = inst.execute(|| {
<Pallet<TestRuntime>>::funding_asset_to_ct_amount(
<Pallet<TestRuntime>>::funding_asset_to_ct_amount_classic(
project_id,
AcceptedFundingAsset::USDT,
USDT_PARTICIPATION,
Expand Down
12 changes: 6 additions & 6 deletions pallets/funding/src/tests/runtime_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ fn funding_asset_to_ct_amount() {
let expected_ct_amount_contribution = 9_315 * CT_UNIT;
inst.execute(|| {
let block_hash = System::block_hash(System::block_number());
let ct_amount = TestRuntime::funding_asset_to_ct_amount(
let ct_amount = TestRuntime::funding_asset_to_ct_amount_classic(
&TestRuntime,
block_hash,
project_id_1,
Expand Down Expand Up @@ -381,7 +381,7 @@ fn funding_asset_to_ct_amount() {
let expected_ct_amount_contribution = 5_714_720_000_000_000_000;
inst.execute(|| {
let block_hash = System::block_hash(System::block_number());
let ct_amount = TestRuntime::funding_asset_to_ct_amount(
let ct_amount = TestRuntime::funding_asset_to_ct_amount_classic(
&TestRuntime,
block_hash,
project_id_2,
Expand Down Expand Up @@ -433,7 +433,7 @@ fn funding_asset_to_ct_amount() {

inst.execute(|| {
let block_hash = System::block_hash(System::block_number());
let ct_amount = TestRuntime::funding_asset_to_ct_amount(
let ct_amount = TestRuntime::funding_asset_to_ct_amount_classic(
&TestRuntime,
block_hash,
project_id_3,
Expand All @@ -455,7 +455,7 @@ fn funding_asset_to_ct_amount() {

inst.execute(|| {
let block_hash = System::block_hash(System::block_number());
let ct_amount = TestRuntime::funding_asset_to_ct_amount(
let ct_amount = TestRuntime::funding_asset_to_ct_amount_classic(
&TestRuntime,
block_hash,
project_id_3,
Expand Down Expand Up @@ -570,7 +570,7 @@ fn calculate_otm_fee() {

let ct_amount = inst
.execute(|| {
TestRuntime::funding_asset_to_ct_amount(
TestRuntime::funding_asset_to_ct_amount_classic(
&TestRuntime,
block_hash,
project_id,
Expand Down Expand Up @@ -752,7 +752,7 @@ fn get_funding_asset_min_max_amounts() {
// This test requires the buyer to have contributed 4500 USD before calling the API
let required_ct = inst
.execute(|| {
TestRuntime::funding_asset_to_ct_amount(
TestRuntime::funding_asset_to_ct_amount_classic(
&TestRuntime,
block_hash,
project_id,
Expand Down
7 changes: 5 additions & 2 deletions runtimes/polimec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1534,8 +1534,11 @@ impl_runtime_apis! {
}

impl pallet_funding::runtime_api::ExtrinsicHelpers<Block, Runtime> for Runtime {
fn funding_asset_to_ct_amount(project_id: ProjectId, asset: AcceptedFundingAsset, asset_amount: Balance) -> Balance {
Funding::funding_asset_to_ct_amount(project_id, asset, asset_amount)
fn funding_asset_to_ct_amount_classic(project_id: ProjectId, asset: AcceptedFundingAsset, asset_amount: Balance) -> Balance {
Funding::funding_asset_to_ct_amount_classic(project_id, asset, asset_amount)
}
fn funding_asset_to_ct_amount_otm(project_id: ProjectId, asset: AcceptedFundingAsset, asset_amount: Balance) -> (Balance, Balance) {
Funding::funding_asset_to_ct_amount_otm(project_id, asset, asset_amount)
}
fn get_next_vesting_schedule_merge_candidates(account: AccountId, hold_reason: RuntimeHoldReason, end_max_delta: Balance) -> Option<(u32, u32)> {
Funding::get_next_vesting_schedule_merge_candidates(account, hold_reason, end_max_delta)
Expand Down

0 comments on commit ee93b3a

Please sign in to comment.