use crate::*;
use sp_staking::{Agent, DelegationInterface, DelegationMigrator, Delegator};
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebugNoBound, PartialEq)]
pub enum StakeStrategyType {
Transfer,
Delegate,
}
#[derive(Clone, Debug)]
pub struct Pool<T>(T);
impl<AccountID> Into<Agent<AccountID>> for Pool<AccountID> {
fn into(self) -> Agent<AccountID> {
Agent::from(self.0)
}
}
impl<T> From<T> for Pool<T> {
fn from(acc: T) -> Self {
Pool(acc)
}
}
impl<T> Pool<T> {
pub fn get(self) -> T {
self.0
}
}
#[derive(Clone, Debug)]
pub struct Member<T>(T);
impl<AccountID> Into<Delegator<AccountID>> for Member<AccountID> {
fn into(self) -> Delegator<AccountID> {
Delegator::from(self.0)
}
}
impl<T> From<T> for Member<T> {
fn from(acc: T) -> Self {
Member(acc)
}
}
impl<T> Member<T> {
pub fn get(self) -> T {
self.0
}
}
pub trait StakeStrategy {
type Balance: frame_support::traits::tokens::Balance;
type AccountId: Clone + core::fmt::Debug;
type CoreStaking: StakingInterface<Balance = Self::Balance, AccountId = Self::AccountId>;
fn strategy_type() -> StakeStrategyType;
fn bonding_duration() -> EraIndex {
Self::CoreStaking::bonding_duration()
}
fn current_era() -> EraIndex {
Self::CoreStaking::current_era()
}
fn minimum_nominator_bond() -> Self::Balance {
Self::CoreStaking::minimum_nominator_bond()
}
fn transferable_balance(
pool_account: Pool<Self::AccountId>,
member_account: Member<Self::AccountId>,
) -> Self::Balance;
fn total_balance(pool_account: Pool<Self::AccountId>) -> Option<Self::Balance>;
fn member_delegation_balance(member_account: Member<Self::AccountId>) -> Option<Self::Balance>;
fn active_stake(pool_account: Pool<Self::AccountId>) -> Self::Balance {
Self::CoreStaking::active_stake(&pool_account.0).unwrap_or_default()
}
fn total_stake(pool_account: Pool<Self::AccountId>) -> Self::Balance {
Self::CoreStaking::total_stake(&pool_account.0).unwrap_or_default()
}
fn pool_strategy(pool_account: Pool<Self::AccountId>) -> StakeStrategyType {
match Self::CoreStaking::is_virtual_staker(&pool_account.0) {
true => StakeStrategyType::Delegate,
false => StakeStrategyType::Transfer,
}
}
fn nominate(
pool_account: Pool<Self::AccountId>,
validators: Vec<Self::AccountId>,
) -> DispatchResult {
Self::CoreStaking::nominate(&pool_account.0, validators)
}
fn chill(pool_account: Pool<Self::AccountId>) -> DispatchResult {
Self::CoreStaking::chill(&pool_account.0)
}
fn pledge_bond(
who: Member<Self::AccountId>,
pool_account: Pool<Self::AccountId>,
reward_account: &Self::AccountId,
amount: Self::Balance,
bond_type: BondType,
) -> DispatchResult;
fn unbond(pool_account: Pool<Self::AccountId>, amount: Self::Balance) -> DispatchResult {
Self::CoreStaking::unbond(&pool_account.0, amount)
}
fn withdraw_unbonded(
pool_account: Pool<Self::AccountId>,
num_slashing_spans: u32,
) -> Result<bool, DispatchError> {
Self::CoreStaking::withdraw_unbonded(pool_account.0, num_slashing_spans)
}
fn member_withdraw(
who: Member<Self::AccountId>,
pool_account: Pool<Self::AccountId>,
amount: Self::Balance,
num_slashing_spans: u32,
) -> DispatchResult;
fn dissolve(pool_account: Pool<Self::AccountId>) -> DispatchResult;
fn pending_slash(pool_account: Pool<Self::AccountId>) -> Self::Balance;
fn member_slash(
who: Member<Self::AccountId>,
pool_account: Pool<Self::AccountId>,
amount: Self::Balance,
maybe_reporter: Option<Self::AccountId>,
) -> DispatchResult;
fn migrate_nominator_to_agent(
pool_account: Pool<Self::AccountId>,
reward_account: &Self::AccountId,
) -> DispatchResult;
fn migrate_delegation(
pool: Pool<Self::AccountId>,
delegator: Member<Self::AccountId>,
value: Self::Balance,
) -> DispatchResult;
#[cfg(feature = "runtime-benchmarks")]
fn nominations(pool_account: Pool<Self::AccountId>) -> Option<Vec<Self::AccountId>> {
Self::CoreStaking::nominations(&pool_account.0)
}
#[cfg(feature = "runtime-benchmarks")]
fn remove_as_agent(_pool: Pool<Self::AccountId>) {
}
}
pub struct TransferStake<T: Config, Staking: StakingInterface>(PhantomData<(T, Staking)>);
impl<T: Config, Staking: StakingInterface<Balance = BalanceOf<T>, AccountId = T::AccountId>>
StakeStrategy for TransferStake<T, Staking>
{
type Balance = BalanceOf<T>;
type AccountId = T::AccountId;
type CoreStaking = Staking;
fn strategy_type() -> StakeStrategyType {
StakeStrategyType::Transfer
}
fn transferable_balance(
pool_account: Pool<Self::AccountId>,
_: Member<Self::AccountId>,
) -> BalanceOf<T> {
T::Currency::balance(&pool_account.0).saturating_sub(Self::active_stake(pool_account))
}
fn total_balance(pool_account: Pool<Self::AccountId>) -> Option<BalanceOf<T>> {
Some(T::Currency::total_balance(&pool_account.get()))
}
fn member_delegation_balance(
_member_account: Member<T::AccountId>,
) -> Option<Staking::Balance> {
None
}
fn pledge_bond(
who: Member<T::AccountId>,
pool_account: Pool<Self::AccountId>,
reward_account: &Self::AccountId,
amount: BalanceOf<T>,
bond_type: BondType,
) -> DispatchResult {
match bond_type {
BondType::Create => {
T::Currency::transfer(&who.0, &pool_account.0, amount, Preservation::Expendable)?;
Staking::bond(&pool_account.0, amount, &reward_account)
},
BondType::Extra => {
T::Currency::transfer(&who.0, &pool_account.0, amount, Preservation::Preserve)?;
Staking::bond_extra(&pool_account.0, amount)
},
}
}
fn member_withdraw(
who: Member<Self::AccountId>,
pool_account: Pool<Self::AccountId>,
amount: BalanceOf<T>,
_num_slashing_spans: u32,
) -> DispatchResult {
T::Currency::transfer(&pool_account.0, &who.0, amount, Preservation::Expendable)?;
Ok(())
}
fn dissolve(pool_account: Pool<Self::AccountId>) -> DispatchResult {
defensive_assert!(
T::Currency::total_balance(&pool_account.clone().get()).is_zero(),
"dissolving pool should not have any balance"
);
T::Currency::set_balance(&pool_account.get(), Zero::zero());
Ok(())
}
fn pending_slash(_: Pool<Self::AccountId>) -> Self::Balance {
Zero::zero()
}
fn member_slash(
_who: Member<Self::AccountId>,
_pool: Pool<Self::AccountId>,
_amount: Staking::Balance,
_maybe_reporter: Option<T::AccountId>,
) -> DispatchResult {
Err(Error::<T>::Defensive(DefensiveError::DelegationUnsupported).into())
}
fn migrate_nominator_to_agent(
_pool: Pool<Self::AccountId>,
_reward_account: &Self::AccountId,
) -> DispatchResult {
Err(Error::<T>::Defensive(DefensiveError::DelegationUnsupported).into())
}
fn migrate_delegation(
_pool: Pool<Self::AccountId>,
_delegator: Member<Self::AccountId>,
_value: Self::Balance,
) -> DispatchResult {
Err(Error::<T>::Defensive(DefensiveError::DelegationUnsupported).into())
}
}
pub struct DelegateStake<T: Config, Staking: StakingInterface, Delegation: DelegationInterface>(
PhantomData<(T, Staking, Delegation)>,
);
impl<
T: Config,
Staking: StakingInterface<Balance = BalanceOf<T>, AccountId = T::AccountId>,
Delegation: DelegationInterface<Balance = BalanceOf<T>, AccountId = T::AccountId>
+ DelegationMigrator<Balance = BalanceOf<T>, AccountId = T::AccountId>,
> StakeStrategy for DelegateStake<T, Staking, Delegation>
{
type Balance = BalanceOf<T>;
type AccountId = T::AccountId;
type CoreStaking = Staking;
fn strategy_type() -> StakeStrategyType {
StakeStrategyType::Delegate
}
fn transferable_balance(
pool_account: Pool<Self::AccountId>,
member_account: Member<Self::AccountId>,
) -> BalanceOf<T> {
Delegation::agent_transferable_balance(pool_account.clone().into())
.defensive_unwrap_or_default()
.min(Delegation::delegator_balance(member_account.into()).unwrap_or_default())
}
fn total_balance(pool_account: Pool<Self::AccountId>) -> Option<BalanceOf<T>> {
Delegation::agent_balance(pool_account.into())
}
fn member_delegation_balance(member_account: Member<T::AccountId>) -> Option<BalanceOf<T>> {
Delegation::delegator_balance(member_account.into())
}
fn pledge_bond(
who: Member<T::AccountId>,
pool_account: Pool<Self::AccountId>,
reward_account: &Self::AccountId,
amount: BalanceOf<T>,
bond_type: BondType,
) -> DispatchResult {
match bond_type {
BondType::Create => {
Delegation::register_agent(pool_account.clone().into(), reward_account)?;
Delegation::delegate(who.into(), pool_account.into(), amount)
},
BondType::Extra => {
Delegation::delegate(who.into(), pool_account.into(), amount)
},
}
}
fn member_withdraw(
who: Member<Self::AccountId>,
pool_account: Pool<Self::AccountId>,
amount: BalanceOf<T>,
num_slashing_spans: u32,
) -> DispatchResult {
Delegation::withdraw_delegation(who.into(), pool_account.into(), amount, num_slashing_spans)
}
fn dissolve(pool_account: Pool<Self::AccountId>) -> DispatchResult {
Delegation::remove_agent(pool_account.into())
}
fn pending_slash(pool_account: Pool<Self::AccountId>) -> Self::Balance {
Delegation::pending_slash(pool_account.into()).defensive_unwrap_or_default()
}
fn member_slash(
who: Member<Self::AccountId>,
pool_account: Pool<Self::AccountId>,
amount: BalanceOf<T>,
maybe_reporter: Option<T::AccountId>,
) -> DispatchResult {
Delegation::delegator_slash(pool_account.into(), who.into(), amount, maybe_reporter)
}
fn migrate_nominator_to_agent(
pool: Pool<Self::AccountId>,
reward_account: &Self::AccountId,
) -> DispatchResult {
Delegation::migrate_nominator_to_agent(pool.into(), reward_account)
}
fn migrate_delegation(
pool: Pool<Self::AccountId>,
delegator: Member<Self::AccountId>,
value: Self::Balance,
) -> DispatchResult {
Delegation::migrate_delegation(pool.into(), delegator.into(), value)
}
#[cfg(feature = "runtime-benchmarks")]
fn remove_as_agent(pool: Pool<Self::AccountId>) {
Delegation::force_kill_agent(pool.into())
}
}