Skip to content

Commit

Permalink
positive externality reward
Browse files Browse the repository at this point in the history
  • Loading branch information
amiyatulu committed Jul 10, 2024
1 parent cd3679e commit 518d7e8
Show file tree
Hide file tree
Showing 12 changed files with 259 additions and 36 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions container-chains/runtime-templates/simple/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ impl pallet_positive_externality::Config for Runtime {
type SharedStorageSource = SharedStorage;
type Currency = Balances;
type SchellingGameSharedSource = SchellingGameShared;
type Reward = ();
}

impl pallet_department_funding::Config for Runtime {
Expand Down
200 changes: 180 additions & 20 deletions custom-pallets/positive-externality/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,28 @@ use frame_support::sp_runtime::traits::Saturating;
use frame_support::sp_runtime::SaturatedConversion;
use frame_support::{dispatch::DispatchResult, ensure};
use frame_support::{
traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons},
traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, ReservableCurrency, WithdrawReasons},
PalletId,
};
use frame_system::pallet_prelude::*;
use pallet_schelling_game_shared::types::{Period, PhaseData, RangePoint, SchellingGameType};
use pallet_schelling_game_shared::types::{
JurorGameResult, Period, PhaseData, RangePoint, SchellingGameType, WinningDecision,
};
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_when_details, new_who_and_when, remove_from_vec, Content, PostId,
WhenDetails, WhenDetailsOf, WhoAndWhen, WhoAndWhenOf,
};
use types::{Incentives, IncentivesMetaData};

use sp_std::prelude::*;
use trait_schelling_game_shared::SchellingGameSharedLink;
use trait_shared_storage::SharedStorageLink;
type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
type BalanceOf<T> = <<T as Config>::Currency as Currency<AccountIdOf<T>>>::Balance;
type PositiveImbalanceOf<T> = <<T as Config>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::PositiveImbalance;
pub type BlockNumberOf<T> = BlockNumberFor<T>;
pub type SumTreeNameType<T> = SumTreeName<AccountIdOf<T>, BlockNumberOf<T>>;

Expand Down Expand Up @@ -72,8 +79,12 @@ pub mod pallet {
RangePoint = RangePoint,
Period = Period,
PhaseData = PhaseData<Self>,
WinningDecision = WinningDecision,
JurorGameResult = JurorGameResult,
>;
type Currency: ReservableCurrency<Self::AccountId>;
/// Handler for the unbalanced increment when rewarding (minting rewards)
type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
}

// The pallet's runtime storage items.
Expand Down Expand Up @@ -129,6 +140,21 @@ pub mod pallet {
pub type ValidationBlock<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberOf<T>, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn incentives_count)]
pub type IncentiveCount<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, Incentives<T>>;

#[pallet::type_value]
pub fn IncentivesMetaValue<T: Config>() -> IncentivesMetaData<T> {
IncentivesMetaData::default()
}

#[pallet::storage]
#[pallet::getter(fn incentives_meta)]
pub type IncentivesMeta<T: Config> =
StorageValue<_, IncentivesMetaData<T>, ValueQuery, IncentivesMetaValue<T>>;

// Pallets use events to inform users when important changes are made.
// https://docs.substrate.io/main-docs/build/events-errors/
#[pallet::event]
Expand All @@ -151,6 +177,9 @@ pub mod pallet {
LessThanMinStake,
CannotStakeNow,
ChoiceOutOfRange,
NotReachedMinimumDecision,
NoIncentiveCount,
AlreadyFunded,
}

// Dispatchable functions allows users to interact with the pallet and invoke state changes.
Expand Down Expand Up @@ -390,30 +419,161 @@ pub mod pallet {

#[pallet::call_index(9)]
#[pallet::weight(0)]
pub fn get_incentives(
pub fn add_incentive_count(
origin: OriginFor<T>,
user_to_calculate: T::AccountId,
) -> DispatchResult {
let _who = ensure_signed(origin)?;
let pe_block_number = <ValidationBlock<T>>::get(user_to_calculate.clone());
let who = ensure_signed(origin)?;
let block_number = <ValidationBlock<T>>::get(user_to_calculate.clone());

let key = SumTreeName::PositiveExternality {
user_address: user_to_calculate.clone(),
block_number: pe_block_number.clone(),
user_address: user_to_calculate,
block_number: block_number.clone(),
};

let phase_data = Self::get_phase_data();
T::SchellingGameSharedSource::get_incentives_score_schelling_helper_link(
key.clone(),
phase_data,
RangePoint::ZeroToFive,
)?;

let score = T::SchellingGameSharedSource::get_mean_value_link(key.clone())?;
// println!("Score {:?}", score);
T::SharedStorageSource::set_positive_externality_link(user_to_calculate, score)?;
let (juror_game_result, stake) =
T::SchellingGameSharedSource::get_result_of_juror_score(
key.clone(),
who.clone(),
RangePoint::ZeroToFive,
)?;

T::SchellingGameSharedSource::add_to_incentives_count(key, who.clone())?;
let incentive_count_option = <IncentiveCount<T>>::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;
}
};
<IncentiveCount<T>>::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<T> =
Incentives::new(number_of_games, winner, loser, stake);
<IncentiveCount<T>>::insert(&who, new_incentives);
}
}

Ok(())
}


// Provide incentives

#[pallet::call_index(10)]
#[pallet::weight(0)]
pub fn get_incentives(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
let incentive_meta = <IncentivesMeta<T>>::get();
let total_games_allowed = incentive_meta.total_number;
let incentive_count_option = <IncentiveCount<T>>::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<T> = Incentives::new(0, 0, 0, 0);
<IncentiveCount<T>>::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 =
<T as pallet::Config>::Currency::deposit_into_existing(&who, balance)
.ok()
.unwrap();
<T as pallet::Config>::Reward::on_unbalanced(r);
// Provide the incentives
} else {
Err(Error::<T>::NotReachedMinimumDecision)?
}
}
None => Err(Error::<T>::NoIncentiveCount)?,
}
Ok(())
}

// #[pallet::call_index(9)]
// #[pallet::weight(0)]
// pub fn get_incentives(
// origin: OriginFor<T>,
// user_to_calculate: T::AccountId,
// ) -> DispatchResult {
// let _who = ensure_signed(origin)?;
// let pe_block_number = <ValidationBlock<T>>::get(user_to_calculate.clone());

// let key = SumTreeName::PositiveExternality {
// user_address: user_to_calculate.clone(),
// block_number: pe_block_number.clone(),
// };

// let phase_data = Self::get_phase_data();
// T::SchellingGameSharedSource::get_incentives_score_schelling_helper_link(
// key.clone(),
// phase_data,
// RangePoint::ZeroToFive,
// )?;

// let score = T::SchellingGameSharedSource::get_mean_value_link(key.clone())?;
// // println!("Score {:?}", score);
// T::SharedStorageSource::set_positive_externality_link(user_to_calculate, score)?;

// Ok(())
// }
}
}
1 change: 1 addition & 0 deletions custom-pallets/positive-externality/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl pallet_template::Config for Test {
type SharedStorageSource = SharedStorage;
type Currency = Balances; // New code
type SchellingGameSharedSource = SchellingGameShared;
type Reward = ();
}

// Build genesis storage according to the mock runtime.
Expand Down
2 changes: 1 addition & 1 deletion custom-pallets/positive-externality/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,6 @@ fn test_commit_and_incentives_vote() {
System::set_block_number(12980260);
assert_ok!(TemplateModule::pass_period(RuntimeOrigin::signed(4), 1));

assert_ok!(TemplateModule::get_incentives(RuntimeOrigin::signed(4), 1));
// assert_ok!(TemplateModule::get_incentives(RuntimeOrigin::signed(4), 1));
})
}
40 changes: 40 additions & 0 deletions custom-pallets/positive-externality/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,43 @@ pub struct PositiveExternalityPostUpdate {
pub content: Option<Content>,
pub hidden: Option<bool>,
}

#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct Incentives<T: Config> {
pub number_of_games: u64,
pub winner: u64,
pub loser: u64,
pub total_stake: u64,
pub start: WhenDetailsOf<T>,
}

impl<T: Config> Incentives<T> {
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::<T>(),
}
}
}

#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct IncentivesMetaData<T: Config> {
pub total_number: u64,
pub disincentive_times: u64,
pub total_block: BlockNumberOf<T>,
}

impl<T: Config> Default for IncentivesMetaData<T> {
fn default() -> Self {
Self {
total_number: 20,
disincentive_times: 15, // its 1.5
total_block: 432000u64.saturated_into::<BlockNumberOf<T>>(), // 30 days = (24*60*60)/6 * 30
}
}
}
14 changes: 11 additions & 3 deletions custom-pallets/schelling-game-shared/src/score_game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ impl<T: Config> Pallet<T> {
key: SumTreeNameType<T>,
who: AccountIdOf<T>,
range_point: RangePoint,
) -> Result<JurorGameResult, DispatchError> {
) -> Result<(JurorGameResult, u64), DispatchError> {
match <PeriodName<T>>::get(&key) {
Some(period) => {
ensure!(period == Period::Execution, Error::<T>::PeriodDontMatch);
Expand All @@ -182,6 +182,14 @@ impl<T: Config> Pallet<T> {
}
let new_mean = Self::get_mean_value(key.clone())?;

let drawn_juror = <DrawnJurors<T>>::get(&key);
let mut stake = 0;
if let Ok(i) = drawn_juror.binary_search_by(|(c, _)| c.cmp(&who.clone())) {
stake = drawn_juror[i].1;
} else {
Err(Error::<T>::StakeDoesNotExists)?
}

let incentives_range = Self::get_incentives_range(range_point);
let reveal_votes = <ScoreVoteCommits<T>>::get(&key, &who);
match reveal_votes {
Expand All @@ -193,9 +201,9 @@ impl<T: Config> Pallet<T> {
&& vote * 1000 <= new_mean.checked_add(incentives_range).unwrap()
{
// get incentives
Ok(JurorGameResult::Won)
Ok((JurorGameResult::Won, stake))
} else {
Ok(JurorGameResult::Lost)
Ok((JurorGameResult::Lost, stake))
}
}
None => Err(Error::<T>::VoteNotRevealed)?,
Expand Down
2 changes: 1 addition & 1 deletion custom-pallets/schelling-game-shared/src/share_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ impl<T: Config> SchellingGameSharedLink for Pallet<T> {
key: Self::SumTreeName,
who: Self::AccountId,
range_point: Self::RangePoint,
) -> Result<JurorGameResult, DispatchError> {
) -> Result<(JurorGameResult, u64), DispatchError> {
Self::get_result_of_juror_score(key, who, range_point)
}

Expand Down
Loading

0 comments on commit 518d7e8

Please sign in to comment.