Skip to content

Commit

Permalink
Merge pull request #977 from galacticcouncil/feat/staking-legacy-support
Browse files Browse the repository at this point in the history
feat: staking - support legacy votes
  • Loading branch information
enthusiastmartin authored Dec 19, 2024
2 parents 71bf42b + 2a83b24 commit 47ec56e
Show file tree
Hide file tree
Showing 14 changed files with 230 additions and 143 deletions.
13 changes: 7 additions & 6 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion pallets/staking/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pallet-staking"
version = "4.0.0"
version = "4.0.1"
authors = ['GalacticCouncil']
edition = "2021"
license = "Apache-2.0"
Expand Down Expand Up @@ -34,6 +34,8 @@ frame-benchmarking = { workspace = true, optional = true }
pallet-conviction-voting= { workspace = true }
pallet-referenda = { workspace = true }

pallet-democracy = {workspace = true}

[dev-dependencies]
pallet-uniques = { workspace = true }
orml-tokens = { workspace = true }
Expand All @@ -58,6 +60,7 @@ std = [
"orml-tokens/std",
"pallet-conviction-voting/std",
"pallet-referenda/std",
"pallet-democracy/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
Expand Down
2 changes: 1 addition & 1 deletion pallets/staking/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn generate_votes<T: Config>(position_id: T::PositionItemId, count: u32) {
votes: votes.try_into().unwrap(),
};

crate::PositionVotes::<T>::insert(position_id, voting);
crate::Votes::<T>::insert(position_id, voting);
}

fn run_periods<T: Config>(periods: u32) {
Expand Down
1 change: 1 addition & 0 deletions pallets/staking/src/integrations.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod conviction_voting;
pub mod democracy_legacy;
18 changes: 9 additions & 9 deletions pallets/staking/src/integrations/conviction_voting.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::pallet::{PositionVotes, Positions, ProcessedVotes};
use crate::pallet::{Positions, Votes, VotesRewarded};
use crate::traits::{GetReferendumState, VestingDetails};
use crate::types::{Action, Balance, Conviction, ReferendumIndex, Vote};
use crate::{Config, Error, Pallet};
Expand Down Expand Up @@ -68,7 +68,7 @@ where
conviction,
};

PositionVotes::<T>::try_mutate(position_id, |voting| -> DispatchResult {
Votes::<T>::try_mutate(position_id, |voting| -> DispatchResult {
match voting.votes.binary_search_by_key(&ref_index, |value| value.0) {
Ok(idx) => {
let _ = sp_std::mem::replace(&mut voting.votes[idx], (ref_index, staking_vote));
Expand All @@ -94,7 +94,7 @@ where
return;
};

let entry = ProcessedVotes::<T>::take(who, ref_index);
let entry = VotesRewarded::<T>::take(who, ref_index);
if entry.is_some() {
// this vote was already processed, just remove it
return;
Expand All @@ -104,12 +104,12 @@ where
if let Some(position) = maybe_position.as_mut() {
let max_position_vote = Conviction::max_multiplier().saturating_mul_int(position.stake);

if let Some(vote_idx) = PositionVotes::<T>::get(position_id)
if let Some(vote_idx) = Votes::<T>::get(position_id)
.votes
.iter()
.position(|(idx, _)| *idx == ref_index)
{
let (ref_idx, vote) = PositionVotes::<T>::get(position_id).votes[vote_idx];
let (ref_idx, vote) = Votes::<T>::get(position_id).votes[vote_idx];
debug_assert_eq!(ref_idx, ref_index, "Referendum index mismatch");
let points =
Pallet::<T>::calculate_points_for_action(Action::DemocracyVote, vote, max_position_vote);
Expand All @@ -119,7 +119,7 @@ where
position.action_points = position.action_points.saturating_add(points);
}
}
PositionVotes::<T>::mutate(position_id, |voting| {
Votes::<T>::mutate(position_id, |voting| {
voting.votes.remove(vote_idx);
});
}
Expand All @@ -131,16 +131,16 @@ where
fn balance_locked_on_unsuccessful_vote(who: &T::AccountId, ref_index: ReferendumIndex) -> Option<Balance> {
let position_id = Pallet::<T>::get_user_position_id(who).ok()??;

if let Some(vote) = ProcessedVotes::<T>::get(who, ref_index) {
if let Some(vote) = VotesRewarded::<T>::get(who, ref_index) {
return Some(vote.amount);
}

let vote_idx = PositionVotes::<T>::get(position_id)
let vote_idx = Votes::<T>::get(position_id)
.votes
.iter()
.position(|(idx, _)| *idx == ref_index)?;

let (ref_idx, vote) = PositionVotes::<T>::get(position_id).votes[vote_idx];
let (ref_idx, vote) = Votes::<T>::get(position_id).votes[vote_idx];
debug_assert_eq!(ref_idx, ref_index, "Referendum index mismatch");
Some(vote.amount)
}
Expand Down
143 changes: 143 additions & 0 deletions pallets/staking/src/integrations/democracy_legacy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use crate::pallet::{PositionVotes, Positions, ProcessedVotes};
use crate::types::{Action, Balance, Conviction, Vote};
use crate::{Config, Pallet};
use frame_support::dispatch::DispatchResult;
use orml_traits::MultiCurrencyExtended;
use pallet_democracy::traits::DemocracyHooks;
use pallet_democracy::{AccountVote, ReferendumIndex};
use sp_core::Get;
use sp_runtime::FixedPointNumber;

pub struct LegacyStakingDemocracy<T>(sp_std::marker::PhantomData<T>);

impl<T: Config> DemocracyHooks<T::AccountId, Balance> for LegacyStakingDemocracy<T>
where
T::Currency: MultiCurrencyExtended<T::AccountId, Amount = i128>,
{
fn on_vote(_who: &T::AccountId, _ref_index: ReferendumIndex, _vote: AccountVote<Balance>) -> DispatchResult {
// Do nothing.
Ok(())
}

fn on_remove_vote(who: &T::AccountId, ref_index: ReferendumIndex, is_finished: Option<bool>) {
let Some(maybe_position_id) = Pallet::<T>::get_user_position_id(who).ok() else {
return;
};

let Some(position_id) = maybe_position_id else {
return;
};

let entry = ProcessedVotes::<T>::take(who, ref_index);
if entry.is_some() {
// this vote was already processed, just remove it
return;
}

let _ = Positions::<T>::try_mutate(position_id, |maybe_position| -> DispatchResult {
if let Some(position) = maybe_position.as_mut() {
let max_position_vote = Conviction::max_multiplier().saturating_mul_int(position.stake);

if let Some(vote_idx) = PositionVotes::<T>::get(position_id)
.votes
.iter()
.position(|(idx, _)| *idx == ref_index)
{
let (ref_idx, vote) = PositionVotes::<T>::get(position_id).votes[vote_idx];
debug_assert_eq!(ref_idx, ref_index, "Referendum index mismatch");
let points =
Pallet::<T>::calculate_points_for_action(Action::DemocracyVote, vote, max_position_vote);
// Add points only if referendum is finished
if let Some(is_finished) = is_finished {
if is_finished {
position.action_points = position.action_points.saturating_add(points);
}
}
PositionVotes::<T>::mutate(position_id, |voting| {
voting.votes.remove(vote_idx);
});
}
}
Ok(())
});
}

fn remove_vote_locks_if_needed(who: &T::AccountId, ref_index: ReferendumIndex) -> Option<Balance> {
let position_id = Pallet::<T>::get_user_position_id(who).ok()??;

if let Some(vote) = ProcessedVotes::<T>::get(who, ref_index) {
return Some(vote.amount);
}

let vote_idx = PositionVotes::<T>::get(position_id)
.votes
.iter()
.position(|(idx, _)| *idx == ref_index)?;

let (ref_idx, vote) = PositionVotes::<T>::get(position_id).votes[vote_idx];
debug_assert_eq!(ref_idx, ref_index, "Referendum index mismatch");
Some(vote.amount)
}

#[cfg(feature = "runtime-benchmarks")]
fn on_vote_worst_case(who: &T::AccountId) {
use crate::LockIdentifier;
#[cfg(not(feature = "std"))]
use codec::alloc::string::ToString;
use frame_system::Origin;
use orml_traits::MultiLockableCurrency;

T::Currency::update_balance(
T::NativeAssetId::get(),
&Pallet::<T>::pot_account_id(),
10_000_000_000_000i128,
)
.unwrap();
Pallet::<T>::initialize_staking(Origin::<T>::Root.into()).unwrap();
T::Currency::update_balance(T::NativeAssetId::get(), who, 1_000_000_000_000_000i128).unwrap();
Pallet::<T>::stake(Origin::<T>::Signed(who.clone()).into(), 1_000_000_000_000_000u128).unwrap();

let position_id = Pallet::<T>::get_user_position_id(&who.clone()).unwrap().unwrap();

let mut votes = sp_std::vec::Vec::<(u32, Vote)>::new();
for i in 0..<T as crate::pallet::Config>::MaxVotes::get() {
votes.push((
i,
Vote {
amount: 20_000_000_000_000_000,
conviction: Conviction::Locked1x,
},
));
}

for i in 0..<T as crate::pallet::Config>::MaxLocks::get() - 5 {
let id: LockIdentifier = scale_info::prelude::format!("{:a>8}", i.to_string())
.as_bytes()
.try_into()
.unwrap();

T::Currency::set_lock(id, T::NativeAssetId::get(), who, 10_000_000_000_000_u128).unwrap();
}

let voting = crate::types::Voting::<T::MaxVotes> {
votes: votes.try_into().unwrap(),
};

crate::PositionVotes::<T>::insert(position_id, voting);
}

#[cfg(feature = "runtime-benchmarks")]
fn on_remove_vote_worst_case(who: &T::AccountId) {
use frame_system::Origin;

T::Currency::update_balance(
T::NativeAssetId::get(),
&Pallet::<T>::pot_account_id(),
10_000_000_000_000i128,
)
.unwrap();
Pallet::<T>::initialize_staking(Origin::<T>::Root.into()).unwrap();
T::Currency::update_balance(T::NativeAssetId::get(), who, 1_000_000_000_000_000i128).unwrap();
Pallet::<T>::stake(Origin::<T>::Signed(who.clone()).into(), 1_000_000_000_000_000u128).unwrap();
}
}
Loading

0 comments on commit 47ec56e

Please sign in to comment.