Skip to content

Commit

Permalink
Merge pull request #678 from galacticcouncil/fix/staking-vested-stake
Browse files Browse the repository at this point in the history
fix: unlock all rewards on claim, updated vote cap and events
  • Loading branch information
martinfridrich authored Oct 9, 2023
2 parents d01df9c + 3f6232d commit d7f9fa5
Show file tree
Hide file tree
Showing 16 changed files with 637 additions and 150 deletions.
12 changes: 6 additions & 6 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "runtime-integration-tests"
version = "1.12.4"
version = "1.13.0"
description = "Integration tests"
authors = ["GalacticCouncil"]
edition = "2021"
Expand Down Expand Up @@ -189,4 +189,4 @@ std = [
# we don't include integration tests when benchmarking feature is enabled
runtime-benchmarks = [
"hydradx-runtime/runtime-benchmarks",
]
]
97 changes: 97 additions & 0 deletions integration-tests/src/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,3 +858,100 @@ fn stake_should_fail_when_tokens_are_already_staked() {
);
});
}

#[test]
fn staking_should_assign_less_action_points_when_portion_of_staking_lock_is_vested() {
TestNet::reset();
Hydra::execute_with(|| {
System::set_block_number(0);
init_omnipool();
assert_ok!(Staking::initialize_staking(RawOrigin::Root.into()));

let staking_account = pallet_staking::Pallet::<hydradx_runtime::Runtime>::pot_account_id();
assert_ok!(Currencies::update_balance(
RawOrigin::Root.into(),
staking_account,
HDX,
(10_000 * UNITS) as i128,
));

assert_ok!(Currencies::update_balance(
RawOrigin::Root.into(),
vesting_account(),
HDX,
(1_000_000 * UNITS) as i128,
));

assert_ok!(Currencies::update_balance(
RawOrigin::Root.into(),
ALICE.into(),
HDX,
(1_000_000 * UNITS) as i128,
));

assert_ok!(Currencies::update_balance(
RawOrigin::Root.into(),
BOB.into(),
HDX,
(99_000 * UNITS) as i128,
));

assert_ok!(Vesting::vested_transfer(
RawOrigin::Root.into(),
BOB.into(),
vesting_schedule()
));

assert_eq!(Currencies::free_balance(HDX, &BOB.into()), 200_000 * UNITS);
assert_ok!(Staking::stake(
hydradx_runtime::RuntimeOrigin::signed(BOB.into()),
100_000 * UNITS
));

//Transfer 50% so there is not enough tokens to satify both locks withou overlay.
assert_ok!(Currencies::transfer(
hydradx_runtime::RuntimeOrigin::signed(BOB.into()),
ALICE.into(),
HDX,
50_000 * UNITS
));

let r = begin_referendum();

assert_ok!(Democracy::vote(
hydradx_runtime::RuntimeOrigin::signed(BOB.into()),
r,
AccountVote::Standard {
vote: Vote {
aye: true,
conviction: Conviction::Locked6x,
},
balance: 150_000 * UNITS,
}
));
end_referendum();

let stake_position_id = pallet_staking::Pallet::<hydradx_runtime::Runtime>::get_user_position_id(
&sp_runtime::AccountId32::from(BOB),
)
.unwrap()
.unwrap();
let position_votes =
pallet_staking::Pallet::<hydradx_runtime::Runtime>::get_position_votes(stake_position_id).votes;

assert_eq!(position_votes.len(), 1);
assert_eq!(
position_votes[0].1,
pallet_staking::types::Vote::new(50_000 * UNITS, pallet_staking::types::Conviction::Locked6x)
);

assert_ok!(Staking::claim(
hydradx_runtime::RuntimeOrigin::signed(BOB.into()),
stake_position_id
));

let position = pallet_staking::Pallet::<hydradx_runtime::Runtime>::positions(stake_position_id).unwrap();

assert_eq!(position.get_action_points(), 50_u128);
});
}
2 changes: 1 addition & 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 = "1.0.1"
version = "2.0.0"
authors = ['GalacticCouncil']
edition = "2021"
license = "Apache-2.0"
Expand Down
35 changes: 28 additions & 7 deletions pallets/staking/src/integrations/democracy.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::pallet::{PositionVotes, Positions};
use crate::traits::DemocracyReferendum;
use crate::traits::{DemocracyReferendum, VestingDetails};
use crate::types::{Balance, Conviction, Vote};
use crate::{Config, Error, Pallet};
use frame_support::defensive;
use frame_support::dispatch::DispatchResult;
use orml_traits::MultiCurrencyExtended;
use orml_traits::{MultiCurrency, MultiCurrencyExtended};
use pallet_democracy::traits::DemocracyHooks;
use pallet_democracy::{AccountVote, ReferendumIndex, ReferendumInfo};
use sp_core::Get;

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

Expand All @@ -28,7 +29,7 @@ where
let e = crate::Error::<T>::InconsistentState(crate::InconsistentStateError::PositionNotFound);
defensive!(e);

//NOTE: This is intetional, user can't recover from this state and we don't want
//NOTE: This is intentional, user can't recover from this state and we don't want
//to block voting.
return Ok(());
}
Expand All @@ -51,8 +52,17 @@ where
Conviction::default()
};

// We are capping vote by min(position stake, user's balance - vested amount - locked
// rewards).
// Sub of vested and lockek rewards is necessary because locks overlay so users may end
// up in the situation where portion of the staking lock is also vested or locked
// rewads and we don't want to assign points for it.
let max_vote = T::Currency::free_balance(T::NativeAssetId::get(), who)
.saturating_sub(T::Vesting::locked(who.clone()))
.saturating_sub(position.accumulated_locked_rewards)
.min(position.stake);
let staking_vote = Vote {
amount: amount.min(position.stake), // use only max staked amount
amount: amount.min(position.stake).min(max_vote),
conviction,
};

Expand Down Expand Up @@ -87,7 +97,7 @@ where
let e = crate::Error::<T>::InconsistentState(crate::InconsistentStateError::PositionNotFound);
defensive!(e);

//NOTE: This is intetional, user can't recover from this state and we don't want
//NOTE: This is intentional, user can't recover from this state and we don't want
//to block voting.
return Ok(());
}
Expand All @@ -102,8 +112,11 @@ where

#[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 sp_core::Get;
use orml_traits::MultiLockableCurrency;

T::Currency::update_balance(
T::NativeAssetId::get(),
Expand All @@ -128,6 +141,15 @@ where
));
}

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(),
};
Expand All @@ -138,7 +160,6 @@ where
#[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::NativeAssetId::get(),
Expand Down
Loading

0 comments on commit d7f9fa5

Please sign in to comment.