From 54701b7da09e71d6e97165d035f2003d19b66e22 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 30 Dec 2024 12:43:03 +0900 Subject: [PATCH] Added some helpers and extrinsic_#1 --- substrate/frame/opf/src/functions.rs | 122 +++++++++++++++++++++++++-- substrate/frame/opf/src/lib.rs | 34 +++++++- substrate/frame/opf/src/types.rs | 4 +- 3 files changed, 150 insertions(+), 10 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 542d73242bd1..1c409e224daf 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -20,13 +20,125 @@ pub use super::*; impl Pallet { + pub fn pot_account() -> AccountIdOf { + // Get Pot account + T::PotId::get().into_account_truncating() + } + + /// Funds transfer from the Pot to a project account + pub fn spend(amount: BalanceOf, beneficiary: AccountIdOf) -> DispatchResult { + // Get Pot account + let pot_account: AccountIdOf = Self::pot_account(); + + //Operate the transfer + T::NativeBalance::transfer(&pot_account, &beneficiary, amount, Preservation::Preserve)?; + + Ok(()) + } + + /// Series of checks on the Pot, to ensure that we have enough funds + /// before executing a Spend --> used in tests. + pub fn pot_check(spend: BalanceOf) -> DispatchResult { + // Get Pot account + let pot_account = Self::pot_account(); + + // Check that the Pot as enough funds for the transfer + let balance = T::NativeBalance::balance(&pot_account); + let minimum_balance = T::NativeBalance::minimum_balance(); + let remaining_balance = balance.saturating_sub(spend); + + ensure!(remaining_balance > minimum_balance, Error::::InsufficientPotReserves); + ensure!(balance > spend, Error::::InsufficientPotReserves); + Ok(()) + } + // Helper function for project registration - pub fn register_new(project_id: ProjectId, amount: BalanceOf) -> DispatchResult{ + pub fn register_new(project_id: ProjectId) -> DispatchResult{ let submission_block = T::BlockNumberProvider::current_block_number(); - let project_infos: ProjectInfo = ProjectInfo { project_id, submission_block, amount}; - let mut bounded = Projects::get(); - let _ = bounded.try_push(project_infos); - Projects::put(bounded); + let mut bounded: BoundedVec, T::MaxProjects> = WhiteListedProjectAccounts::::get(); + let _ = bounded.try_push(project_id); + WhiteListedProjectAccounts::::put(bounded); Ok(()) } + + // Voting Period checks + pub fn period_check() -> DispatchResult { + // Get current voting round & check if we are in voting period or not + let current_round_index = VotingRoundNumber::::get().saturating_sub(1); + let round = VotingRounds::::get(current_round_index).ok_or(Error::::NoRoundFound)?; + let now = T::BlockNumberProvider::current_block_number(); + ensure!(now < round.round_ending_block, Error::::VotingRoundOver); + Ok(()) + } + + pub fn unlist_project(project_id: ProjectId) -> DispatchResult { + WhiteListedProjectAccounts::::mutate(|value| { + let mut val = value.clone(); + val.retain(|x| *x != project_id); + *value = val; + }); + + Ok(()) + } + + // The total reward to be distributed is a portion or inflation, determined in another pallet + // Reward calculation is executed within the Voting period + pub fn calculate_rewards(total_reward: BalanceOf) -> DispatchResult { + let projects = WhiteListedProjectAccounts::::get(); + let round_number = VotingRoundNumber::::get().saturating_sub(1); + let round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; + if projects.clone().len() > 0 as usize { + let total_positive_votes_amount = round.total_positive_votes_amount; + let total_negative_votes_amount = round.total_negative_votes_amount; + + let total_votes_amount = + total_positive_votes_amount.saturating_sub(total_negative_votes_amount); + + // for each project, calculate the percentage of votes, the amount to be distributed, + // and then populate the storage Projects + for project in projects { + if ProjectFunds::::contains_key(&project) { + let funds = ProjectFunds::::get(&project); + let project_positive_reward = funds[0]; + let project_negative_reward = funds[1]; + + let project_reward = + project_positive_reward.saturating_sub(project_negative_reward); + + let project_percentage = + Percent::from_rational(project_reward, total_votes_amount); + let final_amount = project_percentage * total_reward; + + // Send calculated reward for reward distribution + let now = T::BlockNumberProvider::current_block_number(); + let project_info = ProjectInfo { + project_id: project.clone(), + submission_block: now, + amount: final_amount, + }; + + let mut rewarded = Projects::::get(); + rewarded + .try_push(project_info.clone()) + .map_err(|_| Error::::MaximumProjectsNumber)?; + + Projects::::mutate(|value| { + *value = rewarded; + }); + + let when = T::BlockNumberProvider::current_block_number(); + Self::deposit_event(Event::::ProjectFundingAccepted { + project_id: project, + when, + round_number, + amount: project_info.amount, + }) + + } + } + } + + Ok(()) + } + } \ No newline at end of file diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 96e3e4014402..80259c92923a 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -111,6 +111,21 @@ pub mod pallet { #[pallet::storage] pub type Projects = StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; + + /// List of Whitelisted Project registered + #[pallet::storage] + pub type WhiteListedProjectAccounts = + StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; + + /// Returns (positive_funds,negative_funds) of Whitelisted Project accounts + #[pallet::storage] + pub type ProjectFunds = StorageMap< + _, + Twox64Concat, + ProjectId, + BoundedVec, ConstU32<2>>, + ValueQuery, + >; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -183,6 +198,14 @@ pub mod pallet { FundsReserveFailed, /// An invalid result was returned InvalidResult, + /// The reward calculation failed due to an internal error + FailedRewardCalculation, + /// Voting round is over + VotingRoundOver, + /// This voting round does not exists + NoRoundFound, + /// Maximum number of projects submission for reward distribution as been reached + MaximumProjectsNumber, } /*#[pallet::hooks] @@ -197,10 +220,10 @@ pub mod pallet { #[pallet::call_index(0)] #[transactional] - pub fn register_project(origin: OriginFor, project_id: ProjectId, amount: BalanceOf) -> DispatchResult{ + pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult{ let _caller = ensure_signed(origin)?; let when = T::BlockNumberProvider::current_block_number(); - Self::register_new(project_id.clone(), amount)?; + Self::register_new(project_id.clone())?; Self::deposit_event(Event::Projectlisted { when, project_id, @@ -210,7 +233,12 @@ pub mod pallet { #[pallet::call_index(1)] #[transactional] - pub fn unregister_project(origin: OriginFor) -> DispatchResult { + pub fn unregister_project(origin: OriginFor, project_id: ProjectId ) -> DispatchResult { + ensure_root(origin)?; + let when = T::BlockNumberProvider::current_block_number(); + Self::unlist_project(project_id.clone())?; + Self::deposit_event(Event::::ProjectUnlisted { when, project_id }); + Ok(()) } diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index dc3b929d87a6..0a93d3594ed5 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -38,9 +38,9 @@ pub use frame_support::{ pub use pallet_conviction_voting::Conviction; pub use frame_system::{pallet_prelude::*, RawOrigin}; pub use scale_info::prelude::vec::Vec; -pub use sp_runtime::traits::{ +pub use sp_runtime::{traits::{ AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, Zero, -}; +}, Percent}; pub use sp_std::boxed::Box; pub type BalanceOf = <::NativeBalance as fungible::Inspect<