From 252909b233492a1921009bc8e36a9836d12de7f3 Mon Sep 17 00:00:00 2001 From: Amiya Behera Date: Thu, 25 Jul 2024 16:00:27 +0530 Subject: [PATCH] department funding --- .../runtime-templates/simple/src/lib.rs | 1 + custom-pallets/department-funding/src/lib.rs | 173 +++++++++++++++++- custom-pallets/department-funding/src/mock.rs | 1 + .../department-funding/src/types.rs | 40 ++++ .../positive-externality/src/tests.rs | 20 +- 5 files changed, 224 insertions(+), 11 deletions(-) diff --git a/container-chains/runtime-templates/simple/src/lib.rs b/container-chains/runtime-templates/simple/src/lib.rs index d9c2f20..f130e9a 100644 --- a/container-chains/runtime-templates/simple/src/lib.rs +++ b/container-chains/runtime-templates/simple/src/lib.rs @@ -691,6 +691,7 @@ impl pallet_department_funding::Config for Runtime { type SharedStorageSource = SharedStorage; type Currency = Balances; type SchellingGameSharedSource = SchellingGameShared; + type Reward = (); } impl pallet_project_tips::Config for Runtime { diff --git a/custom-pallets/department-funding/src/lib.rs b/custom-pallets/department-funding/src/lib.rs index 6cbc6c8..c262541 100644 --- a/custom-pallets/department-funding/src/lib.rs +++ b/custom-pallets/department-funding/src/lib.rs @@ -29,14 +29,14 @@ use frame_system::pallet_prelude::*; use sp_std::prelude::*; use frame_support::{ - traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, + traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons, OnUnbalanced}, PalletId, }; -use pallet_schelling_game_shared::types::{Period, PhaseData, RangePoint, SchellingGameType}; +use pallet_schelling_game_shared::types::{Period, PhaseData, RangePoint, SchellingGameType, JurorGameResult}; use pallet_sortition_sum_game::types::SumTreeName; use pallet_support::{ - ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, PostId, WhoAndWhen, - WhoAndWhenOf, + ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, PostId, WhoAndWhen, WhenDetailsOf, + WhoAndWhenOf, new_when_details }; use trait_schelling_game_shared::SchellingGameSharedLink; use trait_shared_storage::SharedStorageLink; @@ -45,8 +45,14 @@ use types::{ DepartmentFundingStatus, DepartmentRequiredFund, FundingStatus, TippingName, TippingValue, }; +use types::{Incentives, IncentivesMetaData}; + + type AccountIdOf = ::AccountId; type BalanceOf = <::Currency as Currency>>::Balance; +type PositiveImbalanceOf = <::Currency as Currency< + ::AccountId, +>>::PositiveImbalance; pub type BlockNumberOf = BlockNumberFor; pub type SumTreeNameType = SumTreeName, BlockNumberOf>; type DepartmentId = u64; @@ -80,8 +86,11 @@ pub mod pallet { RangePoint = RangePoint, Period = Period, PhaseData = PhaseData, + JurorGameResult = JurorGameResult, >; type Currency: ReservableCurrency; + type Reward: OnUnbalanced>; + } // The pallet's runtime storage items. @@ -135,6 +144,21 @@ pub mod pallet { DepartmentFundingStatus, FundingStatus>, >; + #[pallet::storage] + #[pallet::getter(fn incentives_count)] + pub type IncentiveCount = + StorageMap<_, Blake2_128Concat, T::AccountId, Incentives>; + + #[pallet::type_value] + pub fn IncentivesMetaValue() -> IncentivesMetaData { + IncentivesMetaData::default() + } + + #[pallet::storage] + #[pallet::getter(fn incentives_meta)] + pub type IncentivesMeta = + StorageValue<_, IncentivesMetaData, ValueQuery, IncentivesMetaValue>; + // Pallets use events to inform users when important changes are made. // https://docs.substrate.io/main-docs/build/events-errors/ #[pallet::event] @@ -175,6 +199,9 @@ pub mod pallet { FundingStatusProcessing, ReapplicationTimeNotReached, ConditionDontMatch, + NotReachedMinimumDecision, + NoIncentiveCount, + AlreadyFunded, } // Check deparment exists, it will done using loose coupling @@ -424,9 +451,30 @@ pub mod pallet { Ok(()) } - #[pallet::call_index(8)] + // #[pallet::call_index(8)] + // #[pallet::weight(0)] + // pub fn get_incentives( + // origin: OriginFor, + // department_required_fund_id: DepartmentRequiredFundId, + // ) -> DispatchResult { + // let who = ensure_signed(origin)?; + // let block_number = + // Self::get_block_number_of_schelling_game(department_required_fund_id)?; + // // let key = SumTreeName::DepartmentRequiredFund { + // // department_required_fund_id, + // // block_number: block_number.clone(), + // // }; + + // let phase_data = Self::get_phase_data(); + // // T::SchellingGameSharedSource::get_incentives_two_choice_helper_link( + // // key, phase_data, who, + // // )?; + // Ok(()) + // } + + #[pallet::call_index(9)] #[pallet::weight(0)] - pub fn get_incentives( + pub fn add_incentive_count( origin: OriginFor, department_required_fund_id: DepartmentRequiredFundId, ) -> DispatchResult { @@ -438,10 +486,115 @@ pub mod pallet { block_number: block_number.clone(), }; - let phase_data = Self::get_phase_data(); - T::SchellingGameSharedSource::get_incentives_two_choice_helper_link( - key, phase_data, who, - )?; + let (juror_game_result, stake) = + T::SchellingGameSharedSource::get_result_of_juror(key.clone(), who.clone())?; + + T::SchellingGameSharedSource::add_to_incentives_count(key, who.clone())?; + let incentive_count_option = >::get(&who); + match incentive_count_option { + Some(mut incentive) => { + match juror_game_result { + JurorGameResult::Won => { + incentive.number_of_games += 1; + incentive.winner += 1; + incentive.total_stake += stake; + } + JurorGameResult::Lost => { + incentive.number_of_games += 1; + incentive.loser += 1; + incentive.total_stake += stake; + } + + JurorGameResult::Draw => { + incentive.number_of_games += 1; + incentive.total_stake += stake; + } + }; + >::mutate(&who, |incentive_option| { + *incentive_option = Some(incentive); + }); + } + None => { + let mut winner = 0; + let mut loser = 0; + match juror_game_result { + JurorGameResult::Won => { + winner = 1; + } + JurorGameResult::Lost => { + loser = 1; + } + JurorGameResult::Draw => {} + }; + let number_of_games = 1; + let new_incentives: Incentives = + Incentives::new(number_of_games, winner, loser, stake); + >::insert(&who, new_incentives); + } + } + + Ok(()) + } + + + #[pallet::call_index(10)] + #[pallet::weight(0)] + pub fn get_incentives(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + let incentive_meta = >::get(); + let total_games_allowed = incentive_meta.total_number; + let incentive_count_option = >::get(&who); + match incentive_count_option { + Some(incentive) => { + let total_number_games = incentive.number_of_games; + if total_number_games >= total_games_allowed { + let new_incentives: Incentives = Incentives::new(0, 0, 0, 0); + >::mutate(&who, |incentive_option| { + *incentive_option = Some(new_incentives); + }); + + let total_win = incentive.winner; + let total_lost = incentive.loser; + + // Define multipliers + let win_multiplier = 10 * 100; + let lost_multiplier = incentive_meta.disincentive_times * 100; + + // Calculate total_win_incentives and total_lost_incentives + let total_win_incentives = total_win.checked_mul(win_multiplier); + let total_lost_incentives = total_lost.checked_mul(lost_multiplier); + + // Calculate total_incentives, handling overflow or negative errors + let total_incentives = match (total_win_incentives, total_lost_incentives) { + (Some(win), Some(lost)) => win.checked_sub(lost).unwrap_or(0), + _ => 0, // If multiplication overflowed, set total_incentives to 0 + }; + + let mut stake = incentive.total_stake; + // Deduct 1% of the stake if total_lost > total_win + if total_lost > total_win { + let stake_deduction = stake / 100; // 1% of the stake + stake = stake.checked_sub(stake_deduction).unwrap_or(stake); + // Safe subtraction + // println!("Stake deducted by 1%: {}", stake); + } + + let total_fund = stake.checked_add(total_incentives).unwrap_or(0); + + let balance = Self::u64_to_balance_saturated(total_fund); + + let r = + ::Currency::deposit_into_existing(&who, balance) + .ok() + .unwrap(); + ::Reward::on_unbalanced(r); + // Provide the incentives + } else { + Err(Error::::NotReachedMinimumDecision)? + } + } + None => Err(Error::::NoIncentiveCount)?, + } Ok(()) } } diff --git a/custom-pallets/department-funding/src/mock.rs b/custom-pallets/department-funding/src/mock.rs index 2fbd026..596663b 100644 --- a/custom-pallets/department-funding/src/mock.rs +++ b/custom-pallets/department-funding/src/mock.rs @@ -72,6 +72,7 @@ impl pallet_template::Config for Test { type SharedStorageSource = SharedStorage; type Currency = Balances; // New code type SchellingGameSharedSource = SchellingGameShared; + type Reward = (); } impl pallet_balances::Config for Test { diff --git a/custom-pallets/department-funding/src/types.rs b/custom-pallets/department-funding/src/types.rs index c20fd33..9a54f18 100644 --- a/custom-pallets/department-funding/src/types.rs +++ b/custom-pallets/department-funding/src/types.rs @@ -48,3 +48,43 @@ pub struct DepartmentFundingStatus { pub block_number: BlockNumber, pub status: FundingStatus, } + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct Incentives { + pub number_of_games: u64, + pub winner: u64, + pub loser: u64, + pub total_stake: u64, + pub start: WhenDetailsOf, +} + +impl Incentives { + pub fn new(number_of_games: u64, winner: u64, loser: u64, stake: u64) -> Self { + Incentives { + number_of_games: number_of_games, + winner: winner, + loser: loser, + total_stake: stake, + start: new_when_details::(), + } + } +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct IncentivesMetaData { + pub total_number: u64, + pub disincentive_times: u64, + pub total_block: BlockNumberOf, +} + +impl Default for IncentivesMetaData { + fn default() -> Self { + Self { + total_number: 20, + disincentive_times: 15, // its 1.5 + total_block: 432000u64.saturated_into::>(), // 30 days = (24*60*60)/6 * 30 + } + } +} diff --git a/custom-pallets/positive-externality/src/tests.rs b/custom-pallets/positive-externality/src/tests.rs index 375a4e1..872ffdf 100644 --- a/custom-pallets/positive-externality/src/tests.rs +++ b/custom-pallets/positive-externality/src/tests.rs @@ -785,7 +785,25 @@ fn schelling_game_incentives_get_test() { full_schelling_game_func(2, 1); - full_schelling_game_func(3, startblock1); + + let balance = Balances::free_balance(2); + println!("{}", balance); + // assert_eq!(300025, balance); + assert_ok!(TemplateModule::release_positive_externality_fund( + RuntimeOrigin::signed(14), + 2 + )); + + let balance = Balances::free_balance(2); + println!("{}", balance); + + + assert_noop!( + TemplateModule::release_positive_externality_fund(RuntimeOrigin::signed(13), 2), + Error::::AlreadyFunded + ); + + full_schelling_game_func(2, startblock1); let incentive_count = TemplateModule::incentives_count(14).unwrap();