Skip to content

Commit

Permalink
schedule merge runtime api
Browse files Browse the repository at this point in the history
  • Loading branch information
JuaniRios committed Sep 4, 2024
1 parent 9fc2c56 commit bc33597
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 22 deletions.
47 changes: 28 additions & 19 deletions pallets/funding/src/functions/6_settlement.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[allow(clippy::wildcard_imports)]
use super::*;
use crate::{traits::VestingDurationCalculation, Balance};
use crate::{traits::VestingDurationCalculation, Balance, HoldReason::Participation};
use frame_support::{
dispatch::DispatchResult,
ensure,
Expand Down Expand Up @@ -156,17 +156,21 @@ impl<T: Config> Pallet<T> {
if funding_success && bid.status != BidStatus::Rejected {
let funding_end_block = project_details.funding_end_block.ok_or(Error::<T>::ImpossibleState)?;

let plmc_vesting_info =
let vesting_info =
Self::calculate_vesting_info(&bid.bidder, bid.multiplier, bid.plmc_bond.saturating_sub(refunded_plmc))
.map_err(|_| Error::<T>::BadMath)?;

VestingOf::<T>::add_release_schedule(
&bid.bidder,
plmc_vesting_info.total_amount,
plmc_vesting_info.amount_per_block,
funding_end_block,
HoldReason::Participation.into(),
)?;
if vesting_info.duration == 1u32.into() {
Self::release_participation_bond(&bid.bidder, vesting_info.total_amount)?;
} else {
VestingOf::<T>::add_release_schedule(
&bid.bidder,
vesting_info.total_amount,
vesting_info.amount_per_block,
funding_end_block,
HoldReason::Participation.into(),
)?;
}

Self::mint_contribution_tokens(project_id, &bid.bidder, final_ct_amount)?;

Expand All @@ -176,7 +180,7 @@ impl<T: Config> Pallet<T> {
bid.id,
ParticipationType::Bid,
final_ct_amount,
plmc_vesting_info.duration,
vesting_info.duration,
)?;

Self::release_funding_asset(
Expand Down Expand Up @@ -250,20 +254,25 @@ impl<T: Config> Pallet<T> {
)?;
} else {
// Calculate the vesting info and add the release schedule
let vest_info = Self::calculate_vesting_info(
let vesting_info = Self::calculate_vesting_info(
&contribution.contributor,
contribution.multiplier,
contribution.plmc_bond,
)
.map_err(|_| Error::<T>::BadMath)?;

VestingOf::<T>::add_release_schedule(
&contribution.contributor,
vest_info.total_amount,
vest_info.amount_per_block,
funding_end_block,
HoldReason::Participation.into(),
)?;
if vesting_info.duration == 1u32.into() {
Self::release_participation_bond(&contribution.contributor, vesting_info.total_amount)?;
} else {
VestingOf::<T>::add_release_schedule(
&contribution.contributor,
vesting_info.total_amount,
vesting_info.amount_per_block,
funding_end_block,
HoldReason::Participation.into(),
)?;
}

// Mint the contribution tokens
Self::mint_contribution_tokens(project_id, &contribution.contributor, contribution.ct_amount)?;

Expand All @@ -282,7 +291,7 @@ impl<T: Config> Pallet<T> {
contribution.id,
ParticipationType::Contribution,
contribution.ct_amount,
vest_info.duration,
vesting_info.duration,
)?;

final_ct_amount = contribution.ct_amount;
Expand Down
3 changes: 2 additions & 1 deletion pallets/funding/src/instantiator/chain_interactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,8 @@ impl<
ProjectStatus::CommunityRound(..) =>
for cont in contributions {
let did = generate_did_from_account(cont.contributor.clone());
let investor_type = InvestorType::Retail;
// We use institutional to be able to test most multipliers.
let investor_type = InvestorType::Institutional;
let params = DoContributeParams::<T> {
contributor: cont.contributor,
project_id,
Expand Down
3 changes: 2 additions & 1 deletion pallets/funding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ pub type ContributionInfoOf<T> =
pub type BucketOf<T> = Bucket<PriceOf<T>>;
pub type WeightInfoOf<T> = <T as Config>::WeightInfo;
pub type VestingOf<T> = pallet_linear_release::Pallet<T>;
pub type BlockNumberToBalanceOf<T> = <T as pallet_linear_release::Config>::BlockNumberToBalance;

pub const PLMC_FOREIGN_ID: u32 = 3344;
pub const PLMC_DECIMALS: u8 = 10;
Expand Down Expand Up @@ -329,7 +330,7 @@ pub mod pallet {
+ Member;

/// The hold reason enum constructed by the construct_runtime macro
type RuntimeHoldReason: From<HoldReason>;
type RuntimeHoldReason: From<HoldReason> + Parameter + MaxEncodedLen + Copy;

/// The origin enum constructed by the construct_runtime macro
type RuntimeOrigin: IsType<<Self as frame_system::Config>::RuntimeOrigin>
Expand Down
5 changes: 5 additions & 0 deletions pallets/funding/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,5 +552,10 @@ sp_api::mock_impl_runtime_apis! {
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 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)
}


}
}
36 changes: 35 additions & 1 deletion pallets/funding/src/runtime_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,15 @@ sp_api::decl_runtime_apis! {
fn projects_by_did(did: Did) -> Vec<ProjectId>;
}

#[api_version(1)]
#[api_version(2)]
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;

/// 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)>;
}
}

Expand Down Expand Up @@ -169,6 +173,36 @@ impl<T: Config> Pallet<T> {
ct_amount
}

pub fn get_next_vesting_schedule_merge_candidates(
account_id: AccountIdOf<T>,
hold_reason: <T as Config>::RuntimeHoldReason,
end_max_delta: Balance,
) -> Option<(u32, u32)> {
let schedules = pallet_linear_release::Vesting::<T>::get(account_id, hold_reason)
.expect("No vesting schedules found")
.to_vec();

let mut inspected_schedules: Vec<(usize, &pallet_linear_release::VestingInfoOf<T>)> = Vec::new();
let now = <frame_system::Pallet<T>>::block_number();
for (i, schedule) in schedules.iter().enumerate() {
if schedule.starting_block > now {
continue;
}

let schedule_end = schedule.ending_block_as_balance::<BlockNumberToBalanceOf<T>>();

for (j, other_schedule) in inspected_schedules.iter() {
let other_end = other_schedule.ending_block_as_balance::<BlockNumberToBalanceOf<T>>();
let end_delta = schedule_end.abs_diff(other_end);
if end_delta <= end_max_delta {
return Some((*j as u32, i as u32));
}
}
inspected_schedules.push((i, schedule));
}
None
}

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
76 changes: 76 additions & 0 deletions pallets/funding/src/tests/runtime_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,82 @@ fn funding_asset_to_ct_amount() {
});
}

#[test]
fn get_next_vesting_schedule_merge_candidates() {
let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext())));
let evaluations = vec![
UserToUSDBalance::new(EVALUATOR_1, 500_000 * USD_UNIT),
UserToUSDBalance::new(EVALUATOR_2, 250_000 * USD_UNIT),
UserToUSDBalance::new(BIDDER_1, 320_000 * USD_UNIT),
];
let bids = vec![
BidParams::new(BIDDER_1, 50_000 * CT_UNIT, 10u8, AcceptedFundingAsset::USDT),
BidParams::new(BIDDER_1, 400_000 * CT_UNIT, 5u8, AcceptedFundingAsset::USDT),
BidParams::new(BIDDER_2, 50_000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT),
];
let remaining_contributions = vec![
ContributionParams::new(BIDDER_1, 1_000 * CT_UNIT, 5u8, AcceptedFundingAsset::USDT),
ContributionParams::new(BIDDER_1, 15_000 * CT_UNIT, 10u8, AcceptedFundingAsset::USDT),
ContributionParams::new(BIDDER_1, 100 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT),
];

let project_id = inst.create_finished_project(
default_project_metadata(ISSUER_1),
ISSUER_1,
None,
evaluations.clone(),
bids.clone(),
default_community_contributions(),
remaining_contributions.clone(),
);
assert_eq!(ProjectStatus::SettlementStarted(FundingOutcome::Success), inst.go_to_next_state(project_id));
inst.execute(|| {
PolimecFunding::settle_evaluation(RuntimeOrigin::signed(BIDDER_1), project_id, BIDDER_1, 2).unwrap();
PolimecFunding::settle_bid(RuntimeOrigin::signed(BIDDER_1), project_id, BIDDER_1, 0).unwrap();
PolimecFunding::settle_bid(RuntimeOrigin::signed(BIDDER_1), project_id, BIDDER_1, 1).unwrap();
PolimecFunding::settle_contribution(RuntimeOrigin::signed(BIDDER_1), project_id, BIDDER_1, 5).unwrap();
PolimecFunding::settle_contribution(RuntimeOrigin::signed(BIDDER_1), project_id, BIDDER_1, 6).unwrap();
PolimecFunding::settle_contribution(RuntimeOrigin::signed(BIDDER_1), project_id, BIDDER_1, 7).unwrap();
});

let hold_reason: mock::RuntimeHoldReason = HoldReason::Participation.into();
let bidder_1_schedules =
inst.execute(|| pallet_linear_release::Vesting::<TestRuntime>::get(BIDDER_1, hold_reason).unwrap().to_vec());
// Evaluations didn't get a vesting schedule
assert_eq!(bidder_1_schedules.len(), 4);
for schedule in bidder_1_schedules.iter() {}

inst.execute(|| {
let block_hash = System::block_hash(System::block_number());
let (idx_1, idx_2) = TestRuntime::get_next_vesting_schedule_merge_candidates(
&TestRuntime,
block_hash,
BIDDER_1,
HoldReason::Participation.into(),
// within 100 blocks
100u128,
)
.unwrap()
.unwrap();
assert_eq!((idx_1, idx_2), (1, 2));

// Merging the two schedules deletes them and creates a new one at the end of the vec.
LinearRelease::merge_schedules(RuntimeOrigin::signed(BIDDER_1), idx_1, idx_2, hold_reason).unwrap();

let (idx_1, idx_2) = TestRuntime::get_next_vesting_schedule_merge_candidates(
&TestRuntime,
block_hash,
BIDDER_1,
HoldReason::Participation.into(),
// within 100 blocks
100u128,
)
.unwrap()
.unwrap();
assert_eq!((idx_1, idx_2), (0, 1));
});
}

#[test]
fn all_project_participations_by_did() {
let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext())));
Expand Down
3 changes: 3 additions & 0 deletions runtimes/polimec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,9 @@ impl_runtime_apis! {
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 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)
}
}

#[cfg(feature = "try-runtime")]
Expand Down

0 comments on commit bc33597

Please sign in to comment.