1use codec::{Decode, Encode, MaxEncodedLen};
2use pallet_permission0_api::Permission0WalletApi;
3use pallet_torus0_api::Torus0Api;
4use polkadot_sdk::{
5 frame_support::{
6 CloneNoBound, DebugNoBound, EqNoBound, PartialEqNoBound, dispatch::DispatchResult, ensure,
7 },
8 frame_system::ensure_signed,
9 polkadot_sdk_frame::prelude::OriginFor,
10};
11use scale_info::TypeInfo;
12
13use crate::{
14 BalanceOf, Config, Error, Event, Pallet, PermissionContract, PermissionDuration, PermissionId,
15 PermissionScope, Permissions, PermissionsByDelegator, RevocationTerms, generate_permission_id,
16 permission::{
17 add_permission_indices,
18 wallet::{WalletScope, WalletScopeType, WalletStake},
19 },
20};
21
22impl<T: Config> Permission0WalletApi<T::AccountId> for Pallet<T> {
23 fn find_active_wallet_permission(
24 delegator: &T::AccountId,
25 ) -> impl Iterator<
26 Item = (
27 PermissionId,
28 pallet_permission0_api::WalletPermission<T::AccountId>,
29 ),
30 > {
31 PermissionsByDelegator::<T>::get(delegator)
32 .into_iter()
33 .filter_map(|pid| {
34 let permission = Permissions::<T>::get(pid)?;
35 let PermissionScope::Wallet(wallet) = permission.scope else {
36 return None;
37 };
38
39 Some((
40 pid,
41 pallet_permission0_api::WalletPermission {
42 recipient: wallet.recipient,
43 r#type: match wallet.r#type {
44 WalletScopeType::Stake(stake) => {
45 pallet_permission0_api::WalletScopeType::Stake {
46 can_transfer_stake: stake.can_transfer_stake,
47 exclusive_stake_access: stake.exclusive_stake_access,
48 }
49 }
50 },
51 },
52 ))
53 })
54 }
55}
56pub(crate) fn delegate_wallet_stake_permission<T: Config>(
57 origin: OriginFor<T>,
58 recipient: T::AccountId,
59 stake_details: WalletStake,
60 duration: PermissionDuration<T>,
61 revocation: RevocationTerms<T>,
62) -> DispatchResult {
63 let delegator = ensure_signed(origin)?;
64 ensure!(delegator != recipient, Error::<T>::SelfPermissionNotAllowed);
65
66 for (_, perm) in Pallet::<T>::find_active_wallet_permission(&delegator) {
67 if stake_details.exclusive_stake_access
68 || matches!(
69 perm.r#type,
70 pallet_permission0_api::WalletScopeType::Stake {
71 exclusive_stake_access: true,
72 ..
73 }
74 )
75 {
76 return Err(Error::<T>::DuplicatePermission.into());
77 }
78 }
79
80 let scope = PermissionScope::Wallet(WalletScope {
81 recipient: recipient.clone(),
82 r#type: WalletScopeType::Stake(stake_details),
83 });
84 let permission_id = generate_permission_id::<T>(&delegator, &scope)?;
85
86 let contract = PermissionContract::<T>::new(
87 delegator,
88 scope,
89 duration,
90 revocation,
91 crate::EnforcementAuthority::None,
92 );
93
94 Permissions::<T>::insert(permission_id, &contract);
95 add_permission_indices::<T>(
96 &contract.delegator,
97 core::iter::once(&recipient),
98 permission_id,
99 )?;
100
101 <Pallet<T>>::deposit_event(Event::PermissionDelegated {
102 delegator: contract.delegator,
103 permission_id,
104 });
105
106 Ok(())
107}
108
109pub(crate) fn execute_wallet_stake_permission<T: Config>(
110 caller: OriginFor<T>,
111 permission_id: PermissionId,
112 op: WalletStakeOperation<T>,
113) -> DispatchResult {
114 let caller = ensure_signed(caller)?;
115 let Some(permission) = Permissions::<T>::get(permission_id) else {
116 return Err(Error::<T>::PermissionNotFound.into());
117 };
118 let PermissionScope::Wallet(wallet) = &permission.scope else {
119 return Err(Error::<T>::UnsupportedPermissionType.into());
120 };
121 #[allow(irrefutable_let_patterns)]
122 let WalletScopeType::Stake(stake) = &wallet.r#type else {
123 return Err(Error::<T>::UnsupportedPermissionType.into());
124 };
125
126 ensure!(
127 caller == wallet.recipient,
128 Error::<T>::NotPermissionRecipient
129 );
130
131 let staker = &permission.delegator;
132
133 match op {
134 WalletStakeOperation::Unstake { staked, amount } => {
135 <T::Torus>::remove_stake(staker, &staked, amount)?;
136 }
137 WalletStakeOperation::Transfer { from, to, amount } => {
138 ensure!(stake.can_transfer_stake, Error::<T>::PermissionNotFound);
139 <T::Torus>::transfer_stake(staker, &from, &to, amount)?;
140 }
141 }
142
143 Ok(())
144}
145
146#[derive(
147 CloneNoBound, DebugNoBound, Encode, Decode, MaxEncodedLen, TypeInfo, PartialEqNoBound, EqNoBound,
148)]
149#[scale_info(skip_type_params(T))]
150pub enum WalletStakeOperation<T: Config> {
151 Unstake {
154 staked: T::AccountId,
155 amount: BalanceOf<T>,
156 },
157 Transfer {
160 from: T::AccountId,
161 to: T::AccountId,
162 amount: BalanceOf<T>,
163 },
164}