Skip to content

Commit

Permalink
vote extrinsic
Browse files Browse the repository at this point in the history
  • Loading branch information
ndkazu committed Jan 11, 2025
1 parent fb17973 commit 6bc36f0
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 69 deletions.
113 changes: 111 additions & 2 deletions substrate/frame/opf/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,110 @@
pub use super::*;
impl<T: Config> Pallet<T> {
// Helper function for voting action. Existing votes are over-written, and Hold is adjusted
pub fn try_vote(
voter_id: VoterId<T>,
project: ProjectId<T>,
amount: BalanceOf<T>,
is_fund: bool,
conviction: Democracy::Conviction,
) -> DispatchResult {
if !ProjectFunds::<T>::contains_key(&project) {
let bounded = BoundedVec::<BalanceOf<T>, ConstU32<2>>::try_from(vec![
BalanceOf::<T>::zero(),
BalanceOf::<T>::zero(),
])
.expect("It works");
ProjectFunds::<T>::insert(&project, bounded);
}

let projects = WhiteListedProjectAccounts::<T>::get(project.clone())

Check failure on line 38 in substrate/frame/opf/src/functions.rs

View workflow job for this annotation

GitHub Actions / cargo-check-all-crate-macos

unused variable: `projects`
.ok_or(Error::<T>::NoProjectAvailable);
let conviction_fund = amount.saturating_add(
amount.saturating_mul(<u8 as From<Democracy::Conviction>>::from(conviction).into()),
);

// Create vote infos and store/adjust them
let round_number = NextVotingRoundNumber::<T>::get().saturating_sub(1);
let mut round = VotingRounds::<T>::get(round_number).ok_or(Error::<T>::NoRoundFound)?;
if is_fund {
round.total_positive_votes_amount =
round.total_positive_votes_amount.saturating_add(conviction_fund);
} else {
round.total_negative_votes_amount =
round.total_negative_votes_amount.saturating_add(conviction_fund);
}

VotingRounds::<T>::mutate(round_number, |val| {
*val = Some(round.clone());
});

let mut new_vote = VoteInfo {
amount,
round: round.clone(),
is_fund,
conviction,
funds_unlock_block: round.round_ending_block,
};

// Update Funds unlock block according to the selected conviction
new_vote.funds_unlock();
if Votes::<T>::contains_key(&project, &voter_id) {
let old_vote = Votes::<T>::get(&project, &voter_id).ok_or(Error::<T>::NoVoteData)?;
let old_amount = old_vote.amount;
let old_conviction = old_vote.conviction;
let old_conviction_amount =
old_amount.saturating_add(old_amount.saturating_mul(
<u8 as From<Democracy::Conviction>>::from(old_conviction).into(),
));
ProjectFunds::<T>::mutate(&project, |val| {
let mut val0 = val.clone().into_inner();
if is_fund {
val0[0] = val0[0 as usize]
.saturating_add(conviction_fund)
.saturating_sub(old_conviction_amount);
} else {
val0[1] = val0[1 as usize]
.saturating_add(conviction_fund)
.saturating_sub(old_conviction_amount);
}
*val = BoundedVec::<BalanceOf<T>, ConstU32<2>>::try_from(val0).expect("It works");
});

Votes::<T>::mutate(&project, &voter_id, |value| {
*value = Some(new_vote);
});

// Adjust locked amount
let total_hold = T::NativeBalance::total_balance_on_hold(&voter_id);
let new_hold = total_hold.saturating_sub(old_amount).saturating_add(amount);
T::NativeBalance::set_on_hold(&HoldReason::FundsReserved.into(), &voter_id, new_hold)?;
} else {
Votes::<T>::insert(&project, &voter_id, new_vote);
ProjectFunds::<T>::mutate(&project, |val| {
let mut val0 = val.clone().into_inner();
if is_fund {
val0[0] = val0[0 as usize].saturating_add(conviction_fund);
} else {
val0[1] = val0[1 as usize].saturating_add(conviction_fund);
}
*val = BoundedVec::<BalanceOf<T>, ConstU32<2>>::try_from(val0).expect("It works");
});
// Lock the necessary amount
T::NativeBalance::hold(&HoldReason::FundsReserved.into(), &voter_id, amount)?;
}
/*
let ref_index =
ReferendumIndexLog::<T>::get(&project).ok_or(Error::<T>::NoProjectAvailable)?;
let vote = Democracy::Vote { aye: is_fund, conviction };
let account_vote = Democracy::AccountVote::Standard{ vote, balance: amount };
Democracy::Pallet::<T>::vote(&voter_id, ref_index, vote)?;*/

Ok(())
}

pub fn pot_account() -> AccountIdOf<T> {
// Get Pot account
T::PotId::get().into_account_truncating()
Expand Down Expand Up @@ -70,7 +174,9 @@ impl<T: Config> Pallet<T> {
// Reward calculation is executed within the Voting period
pub fn calculate_rewards(total_reward: BalanceOf<T>) -> DispatchResult {
let projects: Vec<ProjectId<T>> = WhiteListedProjectAccounts::<T>::iter_keys().collect();
if projects.is_empty() { return Ok(()) }
if projects.is_empty() {
return Ok(())
}
let round_number = NextVotingRoundNumber::<T>::get().saturating_sub(1);
let round = VotingRounds::<T>::get(round_number).ok_or(Error::<T>::NoRoundFound)?;
if projects.clone().len() > 0 as usize {
Expand All @@ -95,12 +201,16 @@ impl<T: Config> Pallet<T> {
let project_percentage =
Percent::from_rational(project_reward, total_votes_amount);
let final_amount = project_percentage * total_reward;
let infos = WhiteListedProjectAccounts::<T>::get(&project_id)
.ok_or(Error::<T>::NoProjectAvailable)?;
let ref_index = infos.index;

// Send calculated reward for reward distribution
let project_info = ProjectInfo {
project_id: project_id.clone(),
submission_block: when,
amount: final_amount,
index: ref_index,
};

// create a spend for project to be rewarded
Expand Down Expand Up @@ -151,7 +261,6 @@ impl<T: Config> Pallet<T> {
// Conditions for reward distribution preparations are:
// - We are at the end of voting_round period
if now > round_ending_block {

// Clear ProjectFunds storage
ProjectFunds::<T>::drain();
// Emmit events
Expand Down
146 changes: 100 additions & 46 deletions substrate/frame/opf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,20 @@ pub mod pallet {
pub trait Config: frame_system::Config + Democracy::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type RuntimeCall: Convert<<Self as Config>::RuntimeCall, <Self as frame_system::Config>::RuntimeCall>
+ Parameter
+ UnfilteredDispatchable<RuntimeOrigin = <Self as frame_system::Config>::RuntimeOrigin>
+ From<Call<Self>>
+ GetDispatchInfo;
+ Parameter
+ UnfilteredDispatchable<RuntimeOrigin = <Self as frame_system::Config>::RuntimeOrigin>
+ From<Call<Self>>
+ GetDispatchInfo;
/// The admin origin that can list and un-list whitelisted projects.
type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// Type to access the Balances Pallet.
type NativeBalance: fungible::Inspect<Self::AccountId>
+ fungible::Mutate<Self::AccountId>
+ fungible::hold::Inspect<Self::AccountId>
+ fungible::hold::Mutate<Self::AccountId, Reason = Self::RuntimeHoldReason>
+ fungible::freeze::Inspect<Self::AccountId>
+ fungible::freeze::Mutate<Self::AccountId>;
+ fungible::Mutate<Self::AccountId>
+ fungible::hold::Inspect<Self::AccountId>
+ fungible::hold::Mutate<Self::AccountId, Reason = Self::RuntimeHoldReason>
+ fungible::freeze::Inspect<Self::AccountId>
+ fungible::freeze::Mutate<Self::AccountId>;

type RuntimeHoldReason: From<HoldReason>;
/// Provider for the block number.
Expand Down Expand Up @@ -116,7 +116,7 @@ pub mod pallet {
/// List of Whitelisted Project registered
#[pallet::storage]
pub type WhiteListedProjectAccounts<T: Config> =
CountedStorageMap<_, Twox64Concat, ProjectId<T>, ProjectInfo<T>, OptionQuery>;
CountedStorageMap<_, Twox64Concat, ProjectId<T>, ProjectInfo<T>, OptionQuery>;

/// Returns (positive_funds,negative_funds) of Whitelisted Project accounts
#[pallet::storage]
Expand All @@ -128,6 +128,18 @@ pub mod pallet {
ValueQuery,
>;

/// Returns Votes Infos against (project_id, voter_id) key
#[pallet::storage]
pub type Votes<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
ProjectId<T>,
Twox64Concat,
VoterId<T>,
VoteInfo<T>,
OptionQuery,
>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Expand Down Expand Up @@ -224,7 +236,9 @@ pub mod pallet {
/// Another project has already been submitted under the same project_id
SubmittedProjectId,
/// Project batch already submitted
BatchAlreadySubmitted
BatchAlreadySubmitted,
NoVoteData,
NotEnoughFunds,
}

#[pallet::hooks]
Expand All @@ -236,7 +250,6 @@ pub mod pallet {

#[pallet::call]
impl<T: Config> Pallet<T> {

/// OPF Projects registration
///
/// ## Dispatch Origin
Expand All @@ -256,20 +269,23 @@ pub mod pallet {
/// ## Events
/// Emits [`Event::<T>::Projectslisted`].
#[pallet::call_index(1)]
pub fn register_projects_batch(origin: OriginFor<T>, projects_id: Vec<ProjectId<T>>) -> DispatchResult {
pub fn register_projects_batch(
origin: OriginFor<T>,
projects_id: Vec<ProjectId<T>>,
) -> DispatchResult {
//T::AdminOrigin::ensure_origin_or_root(origin.clone())?;
let who = T::SubmitOrigin::ensure_origin(origin.clone())?;

Check failure on line 277 in substrate/frame/opf/src/lib.rs

View workflow job for this annotation

GitHub Actions / cargo-check-all-crate-macos

unused variable: `who`
// Only 1 batch submission per round
let mut round_index = NextVotingRoundNumber::<T>::get();

// No active round?
if round_index == 0 {
// Start the first voting round
let _round0 = VotingRoundInfo::<T>::new();
round_index = NextVotingRoundNumber::<T>::get();
}
// No active round?
if round_index == 0 {
// Start the first voting round
let _round0 = VotingRoundInfo::<T>::new();
round_index = NextVotingRoundNumber::<T>::get();
}

let current_round_index = round_index.saturating_sub(1);
let current_round_index = round_index.saturating_sub(1);

let round_infos = VotingRounds::<T>::get(current_round_index).expect("InvalidResult");
// Check no Project batch has been submitted yet
Expand All @@ -280,21 +296,36 @@ pub mod pallet {
let when = T::BlockNumberProvider::current_block_number();
if when >= round_ending_block {
// Create a new round.
let _new_round = VotingRoundInfo::<T>::new();
let _new_round = VotingRoundInfo::<T>::new();
}

for project_id in &projects_id{
for project_id in &projects_id {
ProjectInfo::<T>::new(project_id.clone());
// Prepare the proposal call
let call0: <T as Config>::RuntimeCall = crate::Call::<T>::on_registration {project_id: project_id.clone()}.into();
let call0: <T as Config>::RuntimeCall =
crate::Call::<T>::on_registration { project_id: project_id.clone() }.into();
let call = <T as Config>::RuntimeCall::convert(call0);
let call_f = T::Preimages::bound(call).unwrap();
let call_f = T::Preimages::bound(call)?;
let threshold = Democracy::VoteThreshold::SimpleMajority;
Democracy::Pallet::<T>::propose(origin.clone(), call_f.clone(), T::MinimumDeposit::get())?;
Democracy::Pallet::<T>::internal_start_referendum(call_f,threshold, Zero::zero());

Democracy::Pallet::<T>::propose(
origin.clone(),
call_f.clone(),
T::MinimumDeposit::get(),
)?;
let referendum_index = Democracy::Pallet::<T>::internal_start_referendum(
call_f,
threshold,
Zero::zero(),
);
let mut new_infos = WhiteListedProjectAccounts::<T>::get(&project_id)
.ok_or(Error::<T>::NoProjectAvailable)?;
new_infos.index = referendum_index;

WhiteListedProjectAccounts::<T>::mutate(project_id, |value| {
*value = Some(new_infos);
});
}

Self::deposit_event(Event::Projectslisted { when, projects_id });
Ok(())
}
Expand Down Expand Up @@ -333,7 +364,33 @@ pub mod pallet {

#[pallet::call_index(3)]
#[transactional]
pub fn vote(origin: OriginFor<T>) -> DispatchResult {
pub fn vote(
origin: OriginFor<T>,
project_id: ProjectId<T>,
#[pallet::compact] amount: BalanceOf<T>,
is_fund: bool,
conviction: Democracy::Conviction,
) -> DispatchResult {
let voter = ensure_signed(origin.clone())?;
// Get current voting round & check if we are in voting period or not
Self::period_check()?;
// Check that voter has enough funds to vote
let voter_balance = T::NativeBalance::total_balance(&voter);
ensure!(voter_balance > amount, Error::<T>::NotEnoughFunds);

// Check the available un-holded balance
let voter_holds = T::NativeBalance::total_balance_on_hold(&voter);
let available_funds = voter_balance.saturating_sub(voter_holds);
ensure!(available_funds > amount, Error::<T>::NotEnoughFunds);

let infos = WhiteListedProjectAccounts::<T>::get(&project_id)
.ok_or(Error::<T>::NoProjectAvailable)?;
let ref_index = infos.index;

Check failure on line 388 in substrate/frame/opf/src/lib.rs

View workflow job for this annotation

GitHub Actions / cargo-check-all-crate-macos

unused variable: `ref_index`
let vote = Democracy::Vote { aye: is_fund, conviction };

Check failure on line 389 in substrate/frame/opf/src/lib.rs

View workflow job for this annotation

GitHub Actions / cargo-check-all-crate-macos

unused variable: `vote`
// let account_vote = Democracy::AccountVote::Standard { vote, balance: amount };

// Democracy::Pallet::<T>::vote(origin, ref_index, account_vote)?;

Ok(())
}

Expand Down Expand Up @@ -370,23 +427,20 @@ pub mod pallet {
let now = T::BlockNumberProvider::current_block_number();
let info = Spends::<T>::get(&project_id).ok_or(Error::<T>::InexistentSpend)?;
if now >= info.expire {
Spends::<T>::remove(&project_id);
Self::deposit_event(Event::ExpiredClaim {
expired_when: info.expire,
project_id,
});
Ok(())
} else if now < info.expire {
// transfer the funds
Self::spend(info.amount, project_id.clone())?;
Self::deposit_event(Event::RewardClaimed {
when: now,
amount: info.amount,
project_id: project_id.clone(),
});
Self::unlist_project(project_id)?;
Ok(())
} else {
Spends::<T>::remove(&project_id);
Self::deposit_event(Event::ExpiredClaim { expired_when: info.expire, project_id });
Ok(())
} else if now < info.expire {
// transfer the funds
Self::spend(info.amount, project_id.clone())?;
Self::deposit_event(Event::RewardClaimed {
when: now,
amount: info.amount,
project_id: project_id.clone(),
});
Self::unlist_project(project_id)?;
Ok(())
} else {
Err(DispatchError::Other("Not Claiming Period"))
}
}
Expand Down
Loading

0 comments on commit 6bc36f0

Please sign in to comment.