Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚮 Remove WAP #449

Merged
merged 1 commit into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 19 additions & 33 deletions integration-tests/src/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,24 +158,20 @@ fn pre_wap_bids() -> Vec<(u32, [u8; 32], ParticipationMode, InvestorType, u64, f
]
}

fn wap() -> f64 {
10.30346708
}

#[allow(unused)]
fn post_wap_bids() -> Vec<(u32, [u8; 32], ParticipationMode, InvestorType, u64, f64, AcceptedFundingAsset, f64, f64)> {
// (bid_id, User, Participation mode, Investor type, CTs specified in extrinsic, CT Price, Participation Currency, Final participation currency ticket, PLMC bonded as a consequence)
vec![
(21, SAM, OTM, Professional, 2_000, 10.303467, USDT, 20_916.0382, 22_620.1253),
(22, SAM, OTM, Professional, 2_200, 10.303467, DOT, 4_947.8800, 24_882.1378),
(16, DAVE, OTM, Professional, 1_000, 10.303467, USDT, 10_458.0191, 11_310.0627),
(17, GERALT, Classic(15), Institutional, 500, 10.303467, USDT, 5_151.7335, 1_885.0104),
(18, GEORGE, Classic(20), Institutional, 1_900, 10.303467, USDT, 19_576.5875, 5_372.2798),
(19, GINO, Classic(25), Institutional, 600, 10.303467, USDT, 6_182.0802, 1_357.2075),
(20, STEVE, OTM, Professional, 1_000, 10.303467, USDT, 10_458.0191, 11_310.0627),
(0, BROCK, OTM, Professional, 700, 10.000000, USDC, 7_101.4493, 7_683.8639),
(1, BEN, OTM, Professional, 4_000, 10.000000, USDT, 40_600.0000, 43_907.7936),
(2, BILL, Classic(3), Professional, 3_000, 10.000000, USDC, 29_985.0075, 54_884.7420),
(21, SAM, OTM, Professional, 2_000, 12.0, USDT, 24000.0, 26344.6762),
(22, SAM, OTM, Professional, 2_200, 12.0, DOT, 5677.419355, 28979.1438),
(16, DAVE, OTM, Professional, 1_000, 11.0, USDT, 11000.0, 12074.6432),
(17, GERALT, Classic(15), Institutional, 500, 11.0, USDT, 5500.0, 2012.4405),
(18, GEORGE, Classic(20), Institutional, 1_900, 11.0, USDT, 20900.0, 5735.4555),
(19, GINO, Classic(25), Institutional, 600, 11.0, USDT, 6600.0, 1448.9572),
(20, STEVE, OTM, Professional, 1_000, 11.0, USDT, 11000.0, 12074.6432),
(0, BROCK, OTM, Professional, 700, 10.000000, USDC, 6996.501749, 7_683.8639),
(1, BEN, OTM, Professional, 4_000, 10.000000, USDT, 40_000.0, 43_907.7936),
(2, BILL, Classic(3), Professional, 3_000, 10.000000, USDC, 29985.0075, 54_884.7420),
(3, BRAD, Classic(6), Professional, 700, 10.000000, USDT, 7_000.0000, 6_403.2199),
(4, BROCK, Classic(9), Professional, 3_400, 10.000000, USDT, 34_000.0000, 20_734.2359),
(5, BLAIR, Classic(8), Professional, 1_000, 10.000000, USDT, 10_000.0000, 6_860.5928),
Expand All @@ -187,7 +183,7 @@ fn post_wap_bids() -> Vec<(u32, [u8; 32], ParticipationMode, InvestorType, u64,
(11, BELLA, Classic(1), Professional, 800, 10.000000, USDT, 8_000.0000, 43_907.7936),
(12, BRUCE, Classic(4), Institutional, 3_000, 10.000000, USDT, 30_000.0000, 41_163.5565),
(13, BRENT, Classic(1), Institutional, 8_000, 10.000000, USDT, 80_000.0000, 439_077.9363),
(14, DOUG, OTM, Institutional, 100, 10.000000, USDT, 1_015.0000, 5_488.4742),
(14, DOUG, OTM, Institutional, 100, 10.000000, USDT, 1000.0, 5_488.4742),
(15, DAVE, OTM, Professional, 0, 10.000000, USDT, 0.00, 0.00),
]
}
Expand All @@ -198,7 +194,7 @@ fn cts_minted() -> f64 {
}

fn usd_raised() -> f64 {
502_791.8972
513_400.0000
}

#[allow(unused)]
Expand All @@ -209,7 +205,7 @@ fn ct_fees() -> (f64, f64, f64) {

fn issuer_payouts() -> (f64, f64, f64) {
// (USDT, USDC, DOT)
(430_124.27, 36_981.51, 7_670.46)
(437_000.00, 36_981.51, 8_473.12)
}

fn evaluator_reward_pots() -> (f64, f64, f64, f64) {
Expand Down Expand Up @@ -245,17 +241,17 @@ fn final_payouts() -> Vec<([u8; 32], f64, f64)> {
(DAVE, 1059.661749, 0.00),
(MASON, 35.37757672, 0.00),
(MIKE, 82.74302164, 0.00),
(GERALT, 500.0, 1_885.01),
(GEORGE, 1_900.0, 5_372.28),
(GINO, 600.0, 1_357.21),
(GERALT, 500.0, 2_012.44),
(GEORGE, 1_900.0, 5_735.46),
(GINO, 600.0, 1_448.96),
(STEVE, 1017.159307, 0.0),
(SAM, 4267.09505, 0.0),
]
}

fn otm_fee_recipient_balances() -> (f64, f64, f64) {
// USDT, USDC, DOT
(1233.208025, 104.9475262, 73.12137929)
(1305.0, 104.9475262, 85.16129032)
}

fn otm_treasury_sub_account_plmc_held() -> f64 {
Expand Down Expand Up @@ -462,18 +458,6 @@ fn e2e_test() {

let project_details = inst.get_project_details(project_id);
let project_metadata = inst.get_project_metadata(project_id);
let stored_wap = project_details.weighted_average_price.unwrap();
let expected_wap = PriceProviderOf::<PolimecRuntime>::calculate_decimals_aware_price(
PriceOf::<PolimecRuntime>::from_float(wap()),
USD_DECIMALS,
CT_DECIMALS,
)
.unwrap();
assert_close_enough!(
stored_wap.saturating_mul_int(PLMC),
expected_wap.saturating_mul_int(PLMC),
Perquintill::from_float(0.9999)
);

let actual_cts_minted = polimec_runtime::ContributionTokens::total_issuance(project_id);
let expected_cts_minted = FixedU128::from_float(cts_minted()).saturating_mul_int(CT_UNIT);
Expand Down Expand Up @@ -544,6 +528,8 @@ fn e2e_test() {
);

for (user, ct_rewarded, plmc_bonded) in final_payouts() {
let names = names();

let user: PolimecAccountId = user.into();
let ct_rewarded = FixedU128::from_float(ct_rewarded).saturating_mul_int(CT_UNIT);
let plmc_bonded = FixedU128::from_float(plmc_bonded).saturating_mul_int(PLMC);
Expand Down
13 changes: 8 additions & 5 deletions pallets/funding/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,8 +771,8 @@ mod benchmarks {
}

// We have 3 logic paths
// 1 - Accepted bid with no refunds (i.e. final price <= WAP, no partial acceptance)
// 2 - Accepted bid with refund (i.e. final price > WAP or partial acceptance)
// 1 - Accepted bid with no refunds
// 2 - Accepted bid with refund (i.e. partially accepted bid )
// 3 - Rejected bid (i.e. bid not accepted, everything refunded, no CT/migration)
// Path 2 is the most expensive but not by far, so we only benchmark and charge for this weight
#[benchmark]
Expand All @@ -788,10 +788,14 @@ mod benchmarks {

let project_metadata = default_project_metadata::<T>(issuer.clone());
let increase = project_metadata.minimum_price * PriceOf::<T>::saturating_from_rational(5, 10);
let target_wap = project_metadata.minimum_price + increase;
let target_price = project_metadata.minimum_price + increase;

let mut new_bucket = Pallet::<T>::create_bucket_from_metadata(&project_metadata).unwrap();
new_bucket.current_price = target_price;
new_bucket.amount_left = new_bucket.delta_amount;

let evaluations = inst.generate_successful_evaluations(project_metadata.clone(), 10);
let bids = inst.generate_bids_that_take_price_to(project_metadata.clone(), target_wap);
let bids = inst.generate_bids_from_bucket(project_metadata.clone(), new_bucket, USDT);

let project_id =
inst.create_finished_project(project_metadata.clone(), issuer, None, evaluations, bids.clone());
Expand Down Expand Up @@ -829,7 +833,6 @@ mod benchmarks {
id: bid_to_settle.id,
status: bid_to_settle.status,
final_ct_amount: expected_ct_amount,
final_ct_usd_price: bid_to_settle.original_ct_usd_price,
}
.into(),
);
Expand Down
1 change: 0 additions & 1 deletion pallets/funding/src/functions/1_application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ impl<T: Config> Pallet<T> {
issuer_account: issuer.clone(),
issuer_did: did.clone(),
is_frozen: false,
weighted_average_price: None,
fundraising_target_usd: fundraising_target,
status: ProjectStatus::Application,
round_duration: BlockNumberPair::new(None, None),
Expand Down
4 changes: 1 addition & 3 deletions pallets/funding/src/functions/5_funding_end.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ impl<T: Config> Pallet<T> {
ProjectsInAuctionRound::<T>::put(WeakBoundedVec::force_from(project_ids, None));

let auction_allocation_size = project_metadata.total_allocation_size;
let weighted_average_price = bucket.calculate_wap(auction_allocation_size);
project_details.weighted_average_price = Some(weighted_average_price);

let bucket_price_higher_than_initial = bucket.current_price > bucket.initial_price;
let sold_percent =
Expand All @@ -39,7 +37,7 @@ impl<T: Config> Pallet<T> {

DidWithActiveProjects::<T>::set(issuer_did, None);

let usd_raised = Self::calculate_usd_sold_from_bucket(bucket.clone(), project_metadata.total_allocation_size);
let usd_raised = bucket.calculate_usd_raised(auction_allocation_size);
project_details.funding_amount_reached_usd = usd_raised;
project_details.remaining_contribution_tokens =
if bucket.current_price == bucket.initial_price { bucket.amount_left } else { Zero::zero() };
Expand Down
50 changes: 23 additions & 27 deletions pallets/funding/src/functions/6_settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ impl<T: Config> Pallet<T> {
let project_metadata = ProjectsMetadata::<T>::get(project_id).ok_or(Error::<T>::ProjectMetadataNotFound)?;
let funding_success =
matches!(project_details.status, ProjectStatus::SettlementStarted(FundingOutcome::Success));
let wap = project_details.weighted_average_price.unwrap_or(project_metadata.minimum_price);
let mut bid = Bids::<T>::get(project_id, bid_id).ok_or(Error::<T>::ParticipationNotFound)?;

ensure!(
Expand All @@ -173,8 +172,8 @@ impl<T: Config> Pallet<T> {

// Return the full bid amount to refund if bid is rejected or project failed,
// Return a partial amount if the project succeeded, and the wap > paid price or bid is partially accepted
let BidRefund { final_ct_usd_price, final_ct_amount, refunded_plmc, refunded_funding_asset_amount } =
Self::calculate_refund(&bid, funding_success, wap)?;
let BidRefund { final_ct_amount, refunded_plmc, refunded_funding_asset_amount } =
Self::calculate_refund(&bid, funding_success)?;

Self::release_funding_asset(project_id, &bid.bidder, refunded_funding_asset_amount, bid.funding_asset)?;

Expand Down Expand Up @@ -227,42 +226,39 @@ impl<T: Config> Pallet<T> {
id: bid.id,
status: bid.status,
final_ct_amount,
final_ct_usd_price,
});

Ok(())
}

/// Calculate the amount of funds the bidder should receive back based on the original bid
/// amount and price compared to the final bid amount and price.
fn calculate_refund(
bid: &BidInfoOf<T>,
funding_success: bool,
wap: PriceOf<T>,
) -> Result<BidRefund<T>, DispatchError> {
let final_ct_usd_price = if bid.original_ct_usd_price > wap { wap } else { bid.original_ct_usd_price };
fn calculate_refund(bid: &BidInfoOf<T>, funding_success: bool) -> Result<BidRefund, DispatchError> {
let multiplier: MultiplierOf<T> = bid.mode.multiplier().try_into().map_err(|_| Error::<T>::BadMath)?;
if !funding_success || bid.status == BidStatus::Rejected {
return Ok(BidRefund::<T> {
final_ct_usd_price,
let ct_price = bid.original_ct_usd_price;

match bid.status {
BidStatus::Accepted if funding_success => Ok(BidRefund {
final_ct_amount: bid.original_ct_amount,
refunded_plmc: Zero::zero(),
refunded_funding_asset_amount: Zero::zero(),
}),
BidStatus::PartiallyAccepted(accepted_amount) if funding_success => {
let new_ticket_size = ct_price.checked_mul_int(accepted_amount).ok_or(Error::<T>::BadMath)?;
let new_plmc_bond = Self::calculate_plmc_bond(new_ticket_size, multiplier)?;
let new_funding_asset_amount =
Self::calculate_funding_asset_amount(new_ticket_size, bid.funding_asset)?;
let refunded_plmc = bid.plmc_bond.saturating_sub(new_plmc_bond);
let refunded_funding_asset_amount =
bid.funding_asset_amount_locked.saturating_sub(new_funding_asset_amount);
Ok(BidRefund { final_ct_amount: accepted_amount, refunded_plmc, refunded_funding_asset_amount })
},
_ => Ok(BidRefund {
final_ct_amount: Zero::zero(),
refunded_plmc: bid.plmc_bond,
refunded_funding_asset_amount: bid.funding_asset_amount_locked,
});
}),
}
let final_ct_amount = match bid.status {
BidStatus::Accepted => bid.original_ct_amount,
BidStatus::PartiallyAccepted(accepted_amount) => accepted_amount,
_ => Zero::zero(),
};

let new_ticket_size = final_ct_usd_price.checked_mul_int(final_ct_amount).ok_or(Error::<T>::BadMath)?;
let new_plmc_bond = Self::calculate_plmc_bond(new_ticket_size, multiplier)?;
let new_funding_asset_amount = Self::calculate_funding_asset_amount(new_ticket_size, bid.funding_asset)?;
let refunded_plmc = bid.plmc_bond.saturating_sub(new_plmc_bond);
let refunded_funding_asset_amount = bid.funding_asset_amount_locked.saturating_sub(new_funding_asset_amount);

Ok(BidRefund::<T> { final_ct_usd_price, final_ct_amount, refunded_plmc, refunded_funding_asset_amount })
}

pub fn do_mark_project_as_settled(project_id: ProjectId) -> DispatchResult {
Expand Down
38 changes: 0 additions & 38 deletions pallets/funding/src/functions/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,44 +456,6 @@ impl<T: Config> Pallet<T> {
let funding_asset_decimals = T::FundingCurrency::decimals(funding_asset_id.clone());
<PriceProviderOf<T>>::get_decimals_aware_price(funding_asset_id, USD_DECIMALS, funding_asset_decimals)
}

pub fn calculate_usd_sold_from_bucket(mut bucket: BucketOf<T>, auction_allocation_size: Balance) -> Balance {
if bucket.current_price == bucket.initial_price {
return bucket.initial_price.saturating_mul_int(auction_allocation_size.saturating_sub(bucket.amount_left))
}

let mut total_usd_sold = 0u128;
let wap = bucket.calculate_wap(auction_allocation_size);

let mut total_ct_amount_left = auction_allocation_size;

// Latest bucket will be partially sold
let ct_sold = bucket.delta_amount.saturating_sub(bucket.amount_left);
let usd_sold = wap.saturating_mul_int(ct_sold);
total_usd_sold = total_usd_sold.saturating_add(usd_sold);
total_ct_amount_left = total_ct_amount_left.saturating_sub(ct_sold);
bucket.current_price = bucket.current_price.saturating_sub(bucket.delta_price);

while total_ct_amount_left > 0 {
// If we reached the inital bucket, all the CTs remaining are taken from this bucket
if bucket.current_price == bucket.initial_price {
let ct_sold = total_ct_amount_left;
let usd_sold = bucket.initial_price.saturating_mul_int(ct_sold);
total_usd_sold = total_usd_sold.saturating_add(usd_sold);
break
}

let price_charged = wap.min(bucket.current_price);
let ct_sold = total_ct_amount_left.min(bucket.delta_amount);
let usd_raised = price_charged.saturating_mul_int(ct_sold);
total_usd_sold = total_usd_sold.saturating_add(usd_raised);
total_ct_amount_left = total_ct_amount_left.saturating_sub(ct_sold);

bucket.current_price = bucket.current_price.saturating_sub(bucket.delta_price);
}

total_usd_sold
}
}

pub mod typed_data_v4 {
Expand Down
Loading