diff --git a/pallets/staking/src/integrations/democracy.rs b/pallets/staking/src/integrations/democracy.rs index 51c4e0863..0d49354bb 100644 --- a/pallets/staking/src/integrations/democracy.rs +++ b/pallets/staking/src/integrations/democracy.rs @@ -3,11 +3,10 @@ use crate::traits::DemocracyReferendum; use crate::types::{Balance, Conviction, Vote}; use crate::{Config, Error, Pallet}; use frame_support::dispatch::DispatchResult; -use frame_system::Origin; +use frame_support::traits::DefensiveOption; use orml_traits::MultiCurrencyExtended; use pallet_democracy::traits::DemocracyHooks; use pallet_democracy::{AccountVote, ReferendumIndex, ReferendumInfo}; -use sp_core::Get; pub struct StakingDemocracy(sp_std::marker::PhantomData); @@ -21,48 +20,51 @@ where } else { return Ok(()); }; - let position = if let Some(position) = Positions::::get(position_id) { - position - } else { - return Ok(()); - }; - let amount = vote.balance(); - let conviction = if let AccountVote::Standard { vote, .. } = vote { - match vote.conviction { - pallet_democracy::Conviction::None => Conviction::None, - pallet_democracy::Conviction::Locked1x => Conviction::Locked1x, - pallet_democracy::Conviction::Locked2x => Conviction::Locked2x, - pallet_democracy::Conviction::Locked3x => Conviction::Locked3x, - pallet_democracy::Conviction::Locked4x => Conviction::Locked4x, - pallet_democracy::Conviction::Locked5x => Conviction::Locked5x, - pallet_democracy::Conviction::Locked6x => Conviction::Locked6x, - } - } else { - Conviction::default() - }; + Positions::::try_mutate(position_id, |maybe_position| { + let position = maybe_position + .as_mut() + .defensive_ok_or(crate::Error::::InconsistentState( + crate::InconsistentStateError::PositionNotFound, + ))?; - let staking_vote = Vote { - amount: amount.min(position.stake), // use only max staked amount - conviction, - }; + Pallet::::process_votes(position_id, position)?; - PositionVotes::::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)); - } - Err(idx) => { - voting - .votes - .try_insert(idx, (ref_index, staking_vote)) - .map_err(|_| Error::::MaxVotesReached)?; + let amount = vote.balance(); + let conviction = if let AccountVote::Standard { vote, .. } = vote { + match vote.conviction { + pallet_democracy::Conviction::None => Conviction::None, + pallet_democracy::Conviction::Locked1x => Conviction::Locked1x, + pallet_democracy::Conviction::Locked2x => Conviction::Locked2x, + pallet_democracy::Conviction::Locked3x => Conviction::Locked3x, + pallet_democracy::Conviction::Locked4x => Conviction::Locked4x, + pallet_democracy::Conviction::Locked5x => Conviction::Locked5x, + pallet_democracy::Conviction::Locked6x => Conviction::Locked6x, } - } - Ok(()) - })?; + } else { + Conviction::default() + }; - Ok(()) + let staking_vote = Vote { + amount: amount.min(position.stake), // use only max staked amount + conviction, + }; + + PositionVotes::::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)); + } + Err(idx) => { + voting + .votes + .try_insert(idx, (ref_index, staking_vote)) + .map_err(|_| Error::::MaxVotesReached)?; + } + } + Ok(()) + }) + }) } fn on_remove_vote(who: &T::AccountId, ref_index: ReferendumIndex) -> DispatchResult { @@ -83,6 +85,9 @@ where #[cfg(feature = "runtime-benchmarks")] fn on_vote_worst_case(who: &T::AccountId) { + use frame_system::Origin; + use sp_core::Get; + T::Currency::update_balance( T::HdxAssetId::get(), &Pallet::::pot_account_id(), @@ -92,10 +97,32 @@ where Pallet::::initialize_staking(Origin::::Root.into()).unwrap(); T::Currency::update_balance(T::HdxAssetId::get(), who, 1_000_000_000_000_000i128).unwrap(); Pallet::::stake(Origin::::Signed(who.clone()).into(), 1_000_000_000_000_000u128).unwrap(); + + let position_id = Pallet::::get_user_position_id(&who.clone()).unwrap().unwrap(); + + let mut votes = sp_std::vec::Vec::<(u32, Vote)>::new(); + for i in 0..::MaxVotes::get() { + votes.push(( + i, + Vote { + amount: 20_000_000_000_000_000, + conviction: Conviction::Locked1x, + }, + )); + } + + let voting = crate::types::Voting:: { + votes: votes.try_into().unwrap(), + }; + + crate::PositionVotes::::insert(position_id, voting); } #[cfg(feature = "runtime-benchmarks")] fn on_remove_vote_worst_case(who: &T::AccountId) { + use frame_system::Origin; + use sp_core::Get; + T::Currency::update_balance( T::HdxAssetId::get(), &Pallet::::pot_account_id(), diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index ec5fba84e..66f034806 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -869,7 +869,10 @@ impl Pallet { } } - fn process_votes(position_id: T::PositionItemId, position: &mut Position) -> DispatchResult { + pub(crate) fn process_votes( + position_id: T::PositionItemId, + position: &mut Position, + ) -> DispatchResult { PositionVotes::::mutate(position_id, |voting| { voting.votes.retain(|(ref_idx, vote)| { if T::ReferendumInfo::is_referendum_finished(*ref_idx) { diff --git a/pallets/staking/src/tests/process_votes.rs b/pallets/staking/src/tests/process_votes.rs index 8aed487d6..99ebce5bb 100644 --- a/pallets/staking/src/tests/process_votes.rs +++ b/pallets/staking/src/tests/process_votes.rs @@ -1,8 +1,12 @@ -use crate::types::{Conviction, Vote}; +use crate::{ + integrations::democracy::StakingDemocracy, + types::{Conviction, Vote}, +}; use super::*; use mock::Staking; +use pallet_democracy::{traits::DemocracyHooks, AccountVote}; use pretty_assertions::assert_eq; //NOTE: Referendums with even indexes are finished. @@ -261,3 +265,97 @@ fn process_votes_should_do_nothing_when_referendum_doesnt_exists() { assert_eq!(position_before, position); }); } + +#[test] +fn process_votes_should_work_when_on_vote_is_called() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (ALICE, HDX, 150_000 * ONE), + (BOB, HDX, 250_000 * ONE), + (CHARLIE, HDX, 10_000 * ONE), + (DAVE, HDX, 100_000 * ONE), + ]) + .start_at_block(1_452_987) + .with_initialized_staking() + .with_stakes(vec![ + (ALICE, 100_000 * ONE, 1_452_987, 200_000 * ONE), + (BOB, 120_000 * ONE, 1_452_987, 0), + (CHARLIE, 10_000 * ONE, 1_455_000, 10_000 * ONE), + (DAVE, 10 * ONE, 1_465_000, 1), + ]) + .with_votings(vec![( + 1, + vec![ + ( + 1_u32, + Vote { + amount: 10_000 * ONE, + conviction: Conviction::Locked4x, + }, + ), + ( + 2_u32, + Vote { + amount: 10_000 * ONE, + conviction: Conviction::Locked2x, + }, + ), + ( + 3_u32, + Vote { + amount: 10_000 * ONE, + conviction: Conviction::None, + }, + ), + ( + 4_u32, + Vote { + amount: 230_000 * ONE, + conviction: Conviction::Locked1x, + }, + ), + ( + 8_u32, + Vote { + amount: 230_000 * ONE, + conviction: Conviction::Locked1x, + }, + ), + ( + 6_u32, + Vote { + amount: 2 * ONE, + conviction: Conviction::Locked3x, + }, + ), + ], + )]) + .build() + .execute_with(|| { + let position_id = 1; + let position_before = Staking::positions(position_id).unwrap(); + + //Act + assert_ok!(StakingDemocracy::::on_vote( + &BOB, + 7, + AccountVote::Standard { + balance: 1_000 * ONE, + vote: pallet_democracy::Vote { + aye: true, + conviction: pallet_democracy::Conviction::None + } + } + )); + + //Assert + assert_eq!( + Position { + action_points: 950_008_u128, + ..position_before + }, + Staking::positions(position_id).unwrap() + ); + assert_eq!(PositionVotes::::get(position_id).votes.len(), 3); + }); +}