Skip to content

Commit

Permalink
optimize wap calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
JuaniRios committed May 20, 2024
1 parent 915327e commit 6d3e245
Show file tree
Hide file tree
Showing 14 changed files with 388 additions and 123 deletions.
149 changes: 141 additions & 8 deletions pallets/funding/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1984,7 +1984,7 @@ mod benchmarks {

#[block]
{
Pallet::<T>::do_auction_closing(project_id).unwrap();
Pallet::<T>::do_start_auction_closing(project_id).unwrap();
}
// * validity checks *
// Storage
Expand All @@ -1997,10 +1997,8 @@ mod benchmarks {
);
}

// do_community_funding
// Should be complex due to calling `calculate_weighted_average_price`
#[benchmark]
fn start_community_funding(
fn end_auction_closing(
// Insertion attempts in add_to_update_store. Total amount of storage items iterated through in `ProjectsToUpdate`. Leave one free to make the fn succeed
x: Linear<1, { <T as Config>::MaxProjectsToUpdateInsertionAttempts::get() - 1 }>,
// Accepted Bids
Expand Down Expand Up @@ -2117,20 +2115,20 @@ mod benchmarks {
frame_system::Pallet::<T>::set_block_number(auction_closing_end_block + One::one());
let now = inst.current_block();

let community_end_block = now + T::CommunityFundingDuration::get();
let community_start_block = now + One::one();

let insertion_block_number = community_end_block + One::one();
let insertion_block_number = community_start_block;
fill_projects_to_update::<T>(x, insertion_block_number);

#[block]
{
Pallet::<T>::do_start_community_funding(project_id).unwrap();
Pallet::<T>::do_end_auction_closing(project_id).unwrap();
}

// * validity checks *
// Storage
let stored_details = ProjectsDetails::<T>::get(project_id).unwrap();
assert_eq!(stored_details.status, ProjectStatus::CommunityRound);
assert_eq!(stored_details.status, ProjectStatus::CalculatingWAP);
assert!(
stored_details.phase_transition_points.random_closing_ending.unwrap() <
stored_details.phase_transition_points.auction_closing.end().unwrap()
Expand All @@ -2142,6 +2140,134 @@ mod benchmarks {
assert_eq!(rejected_bids_count, 0);
assert_eq!(accepted_bids_count, y as usize);

// Events
frame_system::Pallet::<T>::assert_last_event(
Event::<T>::ProjectPhaseTransition { project_id, phase: ProjectPhases::CalculatingWAP }.into(),
);
}

// do_community_funding
// Should be complex due to calling `calculate_weighted_average_price`
#[benchmark]
fn start_community_funding(
// Insertion attempts in add_to_update_store. Total amount of storage items iterated through in `ProjectsToUpdate`. Leave one free to make the fn succeed
x: Linear<1, { <T as Config>::MaxProjectsToUpdateInsertionAttempts::get() - 1 }>,
// Accepted Bids
y: Linear<0, { <T as Config>::MaxBidsPerProject::get() }>,
) {
// * setup *
let mut inst = BenchInstantiator::<T>::new(None);
// real benchmark starts at block 0, and we can't call `events()` at block 0
inst.advance_time(1u32.into()).unwrap();

let issuer = account::<AccountIdOf<T>>("issuer", 0, 0);
whitelist_account!(issuer);
let bounded_name = BoundedVec::try_from("Contribution Token TEST".as_bytes().to_vec()).unwrap();
let bounded_symbol = BoundedVec::try_from("CTEST".as_bytes().to_vec()).unwrap();
let metadata_hash = BoundedVec::try_from(IPFS_CID.as_bytes().to_vec()).unwrap();

let project_metadata = ProjectMetadata {
token_information: CurrencyMetadata { name: bounded_name, symbol: bounded_symbol, decimals: CT_DECIMALS },
mainnet_token_max_supply: BalanceOf::<T>::try_from(1_000_000 * CT_UNIT)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
total_allocation_size: BalanceOf::<T>::try_from(1_000_000 * CT_UNIT)
.unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
auction_round_allocation_percentage: Percent::from_percent(50u8),
minimum_price: PriceProviderOf::<T>::calculate_decimals_aware_price(
10u128.into(),
USD_DECIMALS,
CT_DECIMALS,
)
.unwrap(),
bidding_ticket_sizes: BiddingTicketSizes {
professional: TicketSize::new(
BalanceOf::<T>::try_from(5000 * USD_UNIT).unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
None,
),
institutional: TicketSize::new(
BalanceOf::<T>::try_from(5000 * USD_UNIT).unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
None,
),
phantom: Default::default(),
},
contributing_ticket_sizes: ContributingTicketSizes {
retail: TicketSize::new(
BalanceOf::<T>::try_from(USD_UNIT).unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
None,
),
professional: TicketSize::new(
BalanceOf::<T>::try_from(USD_UNIT).unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
None,
),
institutional: TicketSize::new(
BalanceOf::<T>::try_from(USD_UNIT).unwrap_or_else(|_| panic!("Failed to create BalanceOf")),
None,
),
phantom: Default::default(),
},
participation_currencies: vec![AcceptedFundingAsset::USDT].try_into().unwrap(),
funding_destination_account: issuer.clone(),
policy_ipfs_cid: Some(metadata_hash.into()),
};
let project_id =
inst.create_auctioning_project(project_metadata.clone(), issuer.clone(), default_evaluations());

let accepted_bids = (0..y)
.map(|i| {
BidParams::<T>::new(
account::<AccountIdOf<T>>("bidder", 0, i),
(500 * CT_UNIT).into(),
1u8,
AcceptedFundingAsset::USDT,
)
})
.collect_vec();

let plmc_needed_for_bids = inst.calculate_auction_plmc_charged_from_all_bids_made_or_with_bucket(
&accepted_bids,
project_metadata.clone(),
None,
);
let plmc_ed = accepted_bids.accounts().existential_deposits();
let funding_asset_needed_for_bids = inst
.calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket(
&accepted_bids,
project_metadata.clone(),
None,
);

inst.mint_plmc_to(plmc_needed_for_bids);
inst.mint_plmc_to(plmc_ed);
inst.mint_foreign_asset_to(funding_asset_needed_for_bids);

inst.bid_for_users(project_id, accepted_bids).unwrap();

let transition_block = inst.get_update_block(project_id, &UpdateType::AuctionClosingStart).unwrap();
inst.jump_to_block(transition_block);
let transition_block = inst.get_update_block(project_id, &UpdateType::AuctionClosingEnd).unwrap();
inst.jump_to_block(transition_block);
let transition_block = inst.get_update_block(project_id, &UpdateType::CommunityFundingStart).unwrap();
// Block is at automatic transition, but it's not run with on_initialize, we do it manually
frame_system::Pallet::<T>::set_block_number(transition_block);

let now = inst.current_block();
let community_end_block = now + T::CommunityFundingDuration::get() - One::one();

let insertion_block_number = community_end_block + One::one();
fill_projects_to_update::<T>(x, insertion_block_number);

#[block]
{
Pallet::<T>::do_start_community_funding(project_id).unwrap();
}

// * validity checks *
// Storage
let stored_details = ProjectsDetails::<T>::get(project_id).unwrap();
assert_eq!(stored_details.status, ProjectStatus::CommunityRound);
let accepted_bids_count = Bids::<T>::iter_prefix_values((project_id,)).count();
assert_eq!(accepted_bids_count, y as usize);

// Events
frame_system::Pallet::<T>::assert_last_event(
Event::<T>::ProjectPhaseTransition { project_id, phase: ProjectPhases::CommunityFunding }.into(),
Expand Down Expand Up @@ -2711,6 +2837,13 @@ mod benchmarks {
});
}

#[test]
fn bench_end_auction_closing() {
new_test_ext().execute_with(|| {
assert_ok!(PalletFunding::<TestRuntime>::test_end_auction_closing());
});
}

#[test]
fn bench_start_community_funding() {
new_test_ext().execute_with(|| {
Expand Down
1 change: 1 addition & 0 deletions pallets/funding/src/functions/1_application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl<T: Config> Pallet<T> {
total_bonded_plmc: Zero::zero(),
evaluators_outcome: EvaluatorsOutcome::Unchanged,
},
auction_round_info: AuctionRoundInfoOf::<T> { is_oversubscribed: IsOversubscribed::No },
funding_end_block: None,
parachain_id: None,
migration_readiness_check: None,
Expand Down
66 changes: 63 additions & 3 deletions pallets/funding/src/functions/3_auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl<T: Config> Pallet<T> {
/// Later on, `on_initialize` transitions the project into the closing auction round, by calling
/// [`do_auction_closing`](Self::do_auction_closing).
#[transactional]
pub fn do_auction_opening(caller: AccountIdOf<T>, project_id: ProjectId) -> DispatchResultWithPostInfo {
pub fn do_start_auction_opening(caller: AccountIdOf<T>, project_id: ProjectId) -> DispatchResultWithPostInfo {
// * Get variables *
let mut project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
let now = <frame_system::Pallet<T>>::block_number();
Expand Down Expand Up @@ -109,7 +109,7 @@ impl<T: Config> Pallet<T> {
/// Later on, `on_initialize` ends the auction closing round and starts the community round,
/// by calling [`do_community_funding`](Self::do_start_community_funding).
#[transactional]
pub fn do_auction_closing(project_id: ProjectId) -> DispatchResultWithPostInfo {
pub fn do_start_auction_closing(project_id: ProjectId) -> DispatchResultWithPostInfo {
// * Get variables *
let mut project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
let now = <frame_system::Pallet<T>>::block_number();
Expand All @@ -134,7 +134,7 @@ impl<T: Config> Pallet<T> {
// Schedule for automatic check by on_initialize. Success depending on enough funding reached
let insertion_iterations = match Self::add_to_update_store(
closing_end_block + 1u32.into(),
(&project_id, UpdateType::CommunityFundingStart),
(&project_id, UpdateType::AuctionClosingEnd),
) {
Ok(iterations) => iterations,
Err(_iterations) => return Err(Error::<T>::TooManyInsertionAttempts.into()),
Expand All @@ -149,6 +149,66 @@ impl<T: Config> Pallet<T> {
})
}

#[transactional]
pub fn do_end_auction_closing(project_id: ProjectId) -> DispatchResultWithPostInfo {
// * Get variables *
let mut project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
let project_metadata = ProjectsMetadata::<T>::get(project_id).ok_or(Error::<T>::ProjectMetadataNotFound)?;
let now = <frame_system::Pallet<T>>::block_number();
let auction_closing_start_block =
project_details.phase_transition_points.auction_closing.start().ok_or(Error::<T>::TransitionPointNotSet)?;
let auction_closing_end_block =
project_details.phase_transition_points.auction_closing.end().ok_or(Error::<T>::TransitionPointNotSet)?;

// * Validity checks *
ensure!(now > auction_closing_end_block, Error::<T>::TooEarlyForRound);
ensure!(project_details.status == ProjectStatus::AuctionClosing, Error::<T>::IncorrectRound);

// * Calculate new variables *
let end_block = Self::select_random_block(auction_closing_start_block, auction_closing_end_block);

// * Update Storage *
let calculation_result = Self::decide_winning_bids(
project_id,
end_block,
project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size,
);

match calculation_result {
Err(e) => return Err(DispatchErrorWithPostInfo { post_info: ().into(), error: e }),
Ok((accepted_bids_count, rejected_bids_count)) => {
// Get info again after updating it with new price.
project_details.phase_transition_points.random_closing_ending = Some(end_block);
project_details.status = ProjectStatus::CalculatingWAP;
ProjectsDetails::<T>::insert(project_id, project_details);

let insertion_iterations = match Self::add_to_update_store(
now + 1u32.into(),
(&project_id, UpdateType::CommunityFundingStart),
) {
Ok(iterations) => iterations,
Err(_iterations) => return Err(Error::<T>::TooManyInsertionAttempts.into()),
};

// * Emit events *
Self::deposit_event(Event::<T>::ProjectPhaseTransition {
project_id,
phase: ProjectPhases::CalculatingWAP,
});

Ok(PostDispatchInfo {
// TODO: make new benchmark
actual_weight: Some(WeightInfoOf::<T>::start_community_funding(
insertion_iterations,
accepted_bids_count,
rejected_bids_count,
)),
pays_fee: Pays::Yes,
})
},
}
}

/// Bid for a project in the bidding stage.
///
/// # Arguments
Expand Down
21 changes: 12 additions & 9 deletions pallets/funding/src/functions/4_contribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,23 @@ impl<T: Config> Pallet<T> {

// * Validity checks *
ensure!(now > auction_closing_end_block, Error::<T>::TooEarlyForRound);
ensure!(project_details.status == ProjectStatus::AuctionClosing, Error::<T>::IncorrectRound);
ensure!(project_details.status == ProjectStatus::CalculatingWAP, Error::<T>::IncorrectRound);

// * Calculate new variables *
let end_block = Self::select_random_block(auction_closing_start_block, auction_closing_end_block);
let community_start_block = now;
let community_end_block = now.saturating_add(T::CommunityFundingDuration::get()).saturating_sub(One::one());
let auction_allocation_size =
project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size;

// * Update Storage *
let calculation_result = Self::calculate_weighted_average_price(
project_id,
end_block,
project_metadata.auction_round_allocation_percentage * project_metadata.total_allocation_size,
);
let _ = Self::decide_winning_bids(project_id, end_block, auction_allocation_size)?;
let wap_result = Self::calculate_weighted_average_price(project_id);

let mut project_details = ProjectsDetails::<T>::get(project_id).ok_or(Error::<T>::ProjectDetailsNotFound)?;
match calculation_result {
match wap_result {
Err(e) => return Err(DispatchErrorWithPostInfo { post_info: ().into(), error: e }),
Ok((accepted_bids_count, rejected_bids_count)) => {
Ok(winning_bids_count) => {
// Get info again after updating it with new price.
project_details.phase_transition_points.random_closing_ending = Some(end_block);
project_details
Expand All @@ -75,10 +76,12 @@ impl<T: Config> Pallet<T> {
phase: ProjectPhases::CommunityFunding,
});

//TODO: address this
let rejected_bids_count = 0;
Ok(PostDispatchInfo {
actual_weight: Some(WeightInfoOf::<T>::start_community_funding(
insertion_iterations,
accepted_bids_count,
winning_bids_count,
rejected_bids_count,
)),
pays_fee: Pays::Yes,
Expand Down
Loading

0 comments on commit 6d3e245

Please sign in to comment.