1use crate::*;
19use frame_support::traits::tokens::{Fortitude::Polite, Preservation::Expendable};
20use sp_staking::{Agent, DelegationInterface, DelegationMigrator, Delegator};
21
22#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq)]
26pub enum StakeStrategyType {
27 Transfer,
32 Delegate,
34}
35
36#[derive(Clone, Debug)]
40pub struct Pool<T>(T);
41impl<AccountID> Into<Agent<AccountID>> for Pool<AccountID> {
42 fn into(self) -> Agent<AccountID> {
43 Agent::from(self.0)
44 }
45}
46impl<T> From<T> for Pool<T> {
47 fn from(acc: T) -> Self {
48 Pool(acc)
49 }
50}
51
52impl<T> Pool<T> {
53 pub fn get(self) -> T {
54 self.0
55 }
56}
57
58#[derive(Clone, Debug)]
62pub struct Member<T>(T);
63impl<AccountID> Into<Delegator<AccountID>> for Member<AccountID> {
64 fn into(self) -> Delegator<AccountID> {
65 Delegator::from(self.0)
66 }
67}
68impl<T> From<T> for Member<T> {
69 fn from(acc: T) -> Self {
70 Member(acc)
71 }
72}
73
74impl<T> Member<T> {
75 pub fn get(self) -> T {
76 self.0
77 }
78}
79
80pub trait StakeStrategy {
86 type Balance: frame_support::traits::tokens::Balance;
87 type AccountId: Clone + core::fmt::Debug;
88 type CoreStaking: StakingInterface<Balance = Self::Balance, AccountId = Self::AccountId>;
89
90 fn strategy_type() -> StakeStrategyType;
92
93 fn bonding_duration() -> EraIndex {
98 Self::CoreStaking::nominator_bonding_duration()
99 }
100
101 fn current_era() -> EraIndex {
106 Self::CoreStaking::current_era()
107 }
108
109 fn minimum_nominator_bond() -> Self::Balance {
111 Self::CoreStaking::minimum_nominator_bond()
112 }
113
114 fn transferable_balance(
118 pool_account: Pool<Self::AccountId>,
119 member_account: Member<Self::AccountId>,
120 ) -> Self::Balance;
121
122 fn total_balance(pool_account: Pool<Self::AccountId>) -> Option<Self::Balance>;
124
125 fn member_delegation_balance(member_account: Member<Self::AccountId>) -> Option<Self::Balance>;
127
128 fn active_stake(pool_account: Pool<Self::AccountId>) -> Self::Balance {
130 Self::CoreStaking::active_stake(&pool_account.0).unwrap_or_default()
131 }
132
133 fn total_stake(pool_account: Pool<Self::AccountId>) -> Self::Balance {
135 Self::CoreStaking::total_stake(&pool_account.0).unwrap_or_default()
136 }
137
138 fn pool_strategy(pool_account: Pool<Self::AccountId>) -> StakeStrategyType {
143 match Self::CoreStaking::is_virtual_staker(&pool_account.0) {
144 true => StakeStrategyType::Delegate,
145 false => StakeStrategyType::Transfer,
146 }
147 }
148
149 fn nominate(
151 pool_account: Pool<Self::AccountId>,
152 validators: Vec<Self::AccountId>,
153 ) -> DispatchResult {
154 Self::CoreStaking::nominate(&pool_account.0, validators)
155 }
156
157 fn chill(pool_account: Pool<Self::AccountId>) -> DispatchResult {
159 Self::CoreStaking::chill(&pool_account.0)
160 }
161
162 fn pledge_bond(
165 who: Member<Self::AccountId>,
166 pool_account: Pool<Self::AccountId>,
167 reward_account: &Self::AccountId,
168 amount: Self::Balance,
169 bond_type: BondType,
170 ) -> DispatchResult;
171
172 fn unbond(pool_account: Pool<Self::AccountId>, amount: Self::Balance) -> DispatchResult {
174 Self::CoreStaking::unbond(&pool_account.0, amount)
175 }
176
177 fn withdraw_unbonded(
179 pool_account: Pool<Self::AccountId>,
180 num_slashing_spans: u32,
181 ) -> Result<bool, DispatchError> {
182 Self::CoreStaking::withdraw_unbonded(pool_account.0, num_slashing_spans)
183 }
184
185 fn member_withdraw(
187 who: Member<Self::AccountId>,
188 pool_account: Pool<Self::AccountId>,
189 amount: Self::Balance,
190 num_slashing_spans: u32,
191 ) -> DispatchResult;
192
193 fn dissolve(pool_account: Pool<Self::AccountId>) -> DispatchResult;
195
196 fn pending_slash(pool_account: Pool<Self::AccountId>) -> Self::Balance;
198
199 fn member_slash(
201 who: Member<Self::AccountId>,
202 pool_account: Pool<Self::AccountId>,
203 amount: Self::Balance,
204 maybe_reporter: Option<Self::AccountId>,
205 ) -> DispatchResult;
206
207 fn migrate_nominator_to_agent(
212 pool_account: Pool<Self::AccountId>,
213 reward_account: &Self::AccountId,
214 ) -> DispatchResult;
215
216 fn migrate_delegation(
225 pool: Pool<Self::AccountId>,
226 delegator: Member<Self::AccountId>,
227 value: Self::Balance,
228 ) -> DispatchResult;
229
230 #[cfg(feature = "runtime-benchmarks")]
232 fn nominations(pool_account: Pool<Self::AccountId>) -> Option<Vec<Self::AccountId>> {
233 Self::CoreStaking::nominations(&pool_account.0)
234 }
235
236 #[cfg(feature = "runtime-benchmarks")]
241 fn remove_as_agent(_pool: Pool<Self::AccountId>) {
242 }
244}
245
246#[deprecated = "consider migrating to DelegateStake"]
256pub struct TransferStake<T: Config, Staking: StakingInterface>(PhantomData<(T, Staking)>);
257
258#[allow(deprecated)]
259impl<T: Config, Staking: StakingInterface<Balance = BalanceOf<T>, AccountId = T::AccountId>>
260 StakeStrategy for TransferStake<T, Staking>
261{
262 type Balance = BalanceOf<T>;
263 type AccountId = T::AccountId;
264 type CoreStaking = Staking;
265
266 fn strategy_type() -> StakeStrategyType {
267 StakeStrategyType::Transfer
268 }
269
270 fn transferable_balance(
271 pool_account: Pool<Self::AccountId>,
272 _: Member<Self::AccountId>,
273 ) -> BalanceOf<T> {
274 T::Currency::reducible_balance(&pool_account.get(), Expendable, Polite)
276 }
277
278 fn total_balance(pool_account: Pool<Self::AccountId>) -> Option<BalanceOf<T>> {
279 Some(T::Currency::total_balance(&pool_account.get()))
280 }
281
282 fn member_delegation_balance(
283 _member_account: Member<T::AccountId>,
284 ) -> Option<Staking::Balance> {
285 None
287 }
288
289 fn pledge_bond(
290 who: Member<T::AccountId>,
291 pool_account: Pool<Self::AccountId>,
292 reward_account: &Self::AccountId,
293 amount: BalanceOf<T>,
294 bond_type: BondType,
295 ) -> DispatchResult {
296 match bond_type {
297 BondType::Create => {
298 T::Currency::transfer(&who.0, &pool_account.0, amount, Preservation::Expendable)?;
300 Staking::bond(&pool_account.0, amount, &reward_account)
301 },
302 BondType::Extra => {
303 T::Currency::transfer(&who.0, &pool_account.0, amount, Preservation::Preserve)?;
305 Staking::bond_extra(&pool_account.0, amount)
306 },
307 }
308 }
309
310 fn member_withdraw(
311 who: Member<Self::AccountId>,
312 pool_account: Pool<Self::AccountId>,
313 amount: BalanceOf<T>,
314 _num_slashing_spans: u32,
315 ) -> DispatchResult {
316 T::Currency::transfer(&pool_account.0, &who.0, amount, Preservation::Expendable)?;
317
318 Ok(())
319 }
320
321 fn dissolve(pool_account: Pool<Self::AccountId>) -> DispatchResult {
322 defensive_assert!(
323 T::Currency::total_balance(&pool_account.clone().get()).is_zero(),
324 "dissolving pool should not have any balance"
325 );
326
327 T::Currency::set_balance(&pool_account.get(), Zero::zero());
329 Ok(())
330 }
331
332 fn pending_slash(_: Pool<Self::AccountId>) -> Self::Balance {
333 Zero::zero()
335 }
336
337 fn member_slash(
338 _who: Member<Self::AccountId>,
339 _pool: Pool<Self::AccountId>,
340 _amount: Staking::Balance,
341 _maybe_reporter: Option<T::AccountId>,
342 ) -> DispatchResult {
343 Err(Error::<T>::Defensive(DefensiveError::DelegationUnsupported).into())
344 }
345
346 fn migrate_nominator_to_agent(
347 _pool: Pool<Self::AccountId>,
348 _reward_account: &Self::AccountId,
349 ) -> DispatchResult {
350 Err(Error::<T>::Defensive(DefensiveError::DelegationUnsupported).into())
351 }
352
353 fn migrate_delegation(
354 _pool: Pool<Self::AccountId>,
355 _delegator: Member<Self::AccountId>,
356 _value: Self::Balance,
357 ) -> DispatchResult {
358 Err(Error::<T>::Defensive(DefensiveError::DelegationUnsupported).into())
359 }
360}
361
362pub struct DelegateStake<T: Config, Staking: StakingInterface, Delegation: DelegationInterface>(
374 PhantomData<(T, Staking, Delegation)>,
375);
376
377impl<
378 T: Config,
379 Staking: StakingInterface<Balance = BalanceOf<T>, AccountId = T::AccountId>,
380 Delegation: DelegationInterface<Balance = BalanceOf<T>, AccountId = T::AccountId>
381 + DelegationMigrator<Balance = BalanceOf<T>, AccountId = T::AccountId>,
382 > StakeStrategy for DelegateStake<T, Staking, Delegation>
383{
384 type Balance = BalanceOf<T>;
385 type AccountId = T::AccountId;
386 type CoreStaking = Staking;
387
388 fn strategy_type() -> StakeStrategyType {
389 StakeStrategyType::Delegate
390 }
391
392 fn transferable_balance(
393 pool_account: Pool<Self::AccountId>,
394 member_account: Member<Self::AccountId>,
395 ) -> BalanceOf<T> {
396 Delegation::agent_transferable_balance(pool_account.clone().into())
397 .defensive_unwrap_or_default()
399 .min(Delegation::delegator_balance(member_account.into()).unwrap_or_default())
400 }
401
402 fn total_balance(pool_account: Pool<Self::AccountId>) -> Option<BalanceOf<T>> {
403 Delegation::agent_balance(pool_account.into())
404 }
405
406 fn member_delegation_balance(member_account: Member<T::AccountId>) -> Option<BalanceOf<T>> {
407 Delegation::delegator_balance(member_account.into())
408 }
409
410 fn pledge_bond(
411 who: Member<T::AccountId>,
412 pool_account: Pool<Self::AccountId>,
413 reward_account: &Self::AccountId,
414 amount: BalanceOf<T>,
415 bond_type: BondType,
416 ) -> DispatchResult {
417 match bond_type {
418 BondType::Create => {
419 Delegation::register_agent(pool_account.clone().into(), reward_account)?;
421 Delegation::delegate(who.into(), pool_account.into(), amount)
422 },
423 BondType::Extra => {
424 Delegation::delegate(who.into(), pool_account.into(), amount)
426 },
427 }
428 }
429
430 fn member_withdraw(
431 who: Member<Self::AccountId>,
432 pool_account: Pool<Self::AccountId>,
433 amount: BalanceOf<T>,
434 num_slashing_spans: u32,
435 ) -> DispatchResult {
436 Delegation::withdraw_delegation(who.into(), pool_account.into(), amount, num_slashing_spans)
437 }
438
439 fn dissolve(pool_account: Pool<Self::AccountId>) -> DispatchResult {
440 Delegation::remove_agent(pool_account.into())
441 }
442
443 fn pending_slash(pool_account: Pool<Self::AccountId>) -> Self::Balance {
444 Delegation::pending_slash(pool_account.into()).defensive_unwrap_or_default()
445 }
446
447 fn member_slash(
448 who: Member<Self::AccountId>,
449 pool_account: Pool<Self::AccountId>,
450 amount: BalanceOf<T>,
451 maybe_reporter: Option<T::AccountId>,
452 ) -> DispatchResult {
453 Delegation::delegator_slash(pool_account.into(), who.into(), amount, maybe_reporter)
454 }
455
456 fn migrate_nominator_to_agent(
457 pool: Pool<Self::AccountId>,
458 reward_account: &Self::AccountId,
459 ) -> DispatchResult {
460 Delegation::migrate_nominator_to_agent(pool.into(), reward_account)
461 }
462
463 fn migrate_delegation(
464 pool: Pool<Self::AccountId>,
465 delegator: Member<Self::AccountId>,
466 value: Self::Balance,
467 ) -> DispatchResult {
468 Delegation::migrate_delegation(pool.into(), delegator.into(), value)
469 }
470
471 #[cfg(feature = "runtime-benchmarks")]
472 fn remove_as_agent(pool: Pool<Self::AccountId>) {
473 Delegation::force_kill_agent(pool.into())
474 }
475}