pallet_delegated_staking/
types.rs1use super::*;
21use frame_support::traits::DefensiveSaturating;
22
23#[derive(Encode, Decode)]
25pub(crate) enum AccountType {
26 ProxyDelegator,
30}
31
32#[derive(Default, Encode, Clone, Decode, Debug, TypeInfo, MaxEncodedLen)]
34#[scale_info(skip_type_params(T))]
35pub struct Delegation<T: Config> {
36 pub agent: T::AccountId,
38 pub amount: BalanceOf<T>,
40}
41
42impl<T: Config> Delegation<T> {
43 pub(crate) fn get(delegator: &T::AccountId) -> Option<Self> {
45 <Delegators<T>>::get(delegator)
46 }
47
48 pub(crate) fn new(agent: &T::AccountId, amount: BalanceOf<T>) -> Self {
50 Delegation { agent: agent.clone(), amount }
51 }
52
53 pub(crate) fn can_delegate(delegator: &T::AccountId, agent: &T::AccountId) -> bool {
58 Delegation::<T>::get(delegator)
59 .map(|delegation| delegation.agent == *agent)
60 .unwrap_or(
61 !<Agents<T>>::contains_key(delegator),
63 )
64 }
65
66 pub(crate) fn update(self, key: &T::AccountId) {
71 let exists = <Delegators<T>>::contains_key(key);
72
73 if self.amount.is_zero() {
74 if exists {
75 <Delegators<T>>::remove(key);
76 let _ = frame_system::Pallet::<T>::dec_providers(key).defensive();
78 }
79 return;
80 }
81
82 if !exists {
83 frame_system::Pallet::<T>::inc_providers(key);
85 }
86
87 <Delegators<T>>::insert(key, self);
88 }
89}
90
91#[derive(Default, Clone, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
97#[scale_info(skip_type_params(T))]
98pub struct AgentLedger<T: Config> {
99 pub payee: T::AccountId,
101 #[codec(compact)]
103 pub total_delegated: BalanceOf<T>,
104 #[codec(compact)]
110 pub unclaimed_withdrawals: BalanceOf<T>,
111 #[codec(compact)]
113 pub pending_slash: BalanceOf<T>,
114}
115
116impl<T: Config> AgentLedger<T> {
117 pub(crate) fn new(reward_destination: &T::AccountId) -> Self {
119 AgentLedger {
120 payee: reward_destination.clone(),
121 total_delegated: Zero::zero(),
122 unclaimed_withdrawals: Zero::zero(),
123 pending_slash: Zero::zero(),
124 }
125 }
126
127 pub(crate) fn get(key: &T::AccountId) -> Option<Self> {
129 <Agents<T>>::get(key)
130 }
131
132 pub(crate) fn update(self, key: &T::AccountId) {
136 <Agents<T>>::insert(key, self)
137 }
138
139 pub(crate) fn remove(key: &T::AccountId) {
141 debug_assert!(<Agents<T>>::contains_key(key), "Agent should exist in storage");
142 <Agents<T>>::remove(key);
143 }
144
145 pub(crate) fn effective_balance(&self) -> BalanceOf<T> {
149 defensive_assert!(
150 self.total_delegated >= self.pending_slash,
151 "slash cannot be higher than actual balance of delegator"
152 );
153
154 self.total_delegated.saturating_sub(self.pending_slash)
156 }
157
158 pub(crate) fn stakeable_balance(&self) -> BalanceOf<T> {
160 self.effective_balance().saturating_sub(self.unclaimed_withdrawals)
161 }
162}
163
164#[derive(Clone)]
166pub struct AgentLedgerOuter<T: Config> {
167 pub key: T::AccountId,
169 pub ledger: AgentLedger<T>,
171}
172
173impl<T: Config> AgentLedgerOuter<T> {
174 pub(crate) fn get(agent: &T::AccountId) -> Result<AgentLedgerOuter<T>, DispatchError> {
176 let ledger = AgentLedger::<T>::get(agent).ok_or(Error::<T>::NotAgent)?;
177 Ok(AgentLedgerOuter { key: agent.clone(), ledger })
178 }
179
180 pub(crate) fn remove_unclaimed_withdraw(
185 self,
186 amount: BalanceOf<T>,
187 ) -> Result<Self, DispatchError> {
188 let new_total_delegated = self
189 .ledger
190 .total_delegated
191 .checked_sub(&amount)
192 .defensive_ok_or(ArithmeticError::Overflow)?;
193 let new_unclaimed_withdrawals = self
194 .ledger
195 .unclaimed_withdrawals
196 .checked_sub(&amount)
197 .defensive_ok_or(ArithmeticError::Overflow)?;
198
199 Ok(AgentLedgerOuter {
200 ledger: AgentLedger {
201 total_delegated: new_total_delegated,
202 unclaimed_withdrawals: new_unclaimed_withdrawals,
203 ..self.ledger
204 },
205 ..self
206 })
207 }
208
209 pub(crate) fn add_unclaimed_withdraw(
211 self,
212 amount: BalanceOf<T>,
213 ) -> Result<Self, DispatchError> {
214 let new_unclaimed_withdrawals = self
215 .ledger
216 .unclaimed_withdrawals
217 .checked_add(&amount)
218 .defensive_ok_or(ArithmeticError::Overflow)?;
219
220 Ok(AgentLedgerOuter {
221 ledger: AgentLedger { unclaimed_withdrawals: new_unclaimed_withdrawals, ..self.ledger },
222 ..self
223 })
224 }
225
226 pub(crate) fn available_to_bond(&self) -> BalanceOf<T> {
231 let bonded_stake = self.bonded_stake();
232 let stakeable = self.ledger.stakeable_balance();
233
234 defensive_assert!(
235 stakeable >= bonded_stake,
236 "cannot be bonded with more than total amount delegated to agent"
237 );
238
239 stakeable.saturating_sub(bonded_stake)
240 }
241
242 pub(crate) fn remove_slash(self, amount: BalanceOf<T>) -> Self {
244 let pending_slash = self.ledger.pending_slash.defensive_saturating_sub(amount);
245 let total_delegated = self.ledger.total_delegated.defensive_saturating_sub(amount);
246
247 AgentLedgerOuter {
248 ledger: AgentLedger { pending_slash, total_delegated, ..self.ledger },
249 ..self
250 }
251 }
252
253 pub(crate) fn bonded_stake(&self) -> BalanceOf<T> {
255 T::CoreStaking::total_stake(&self.key).unwrap_or(Zero::zero())
256 }
257
258 pub(crate) fn is_bonded(&self) -> bool {
260 T::CoreStaking::stake(&self.key).is_ok()
261 }
262
263 pub(crate) fn reward_account(&self) -> &T::AccountId {
265 &self.ledger.payee
266 }
267
268 pub(crate) fn save(self) {
270 let key = self.key;
271 self.ledger.update(&key)
272 }
273
274 pub(crate) fn update(self) {
276 let key = self.key;
277 self.ledger.update(&key);
278 }
279
280 pub(crate) fn reload(self) -> Result<AgentLedgerOuter<T>, DispatchError> {
282 Self::get(&self.key)
283 }
284
285 #[cfg(test)]
290 pub(crate) fn total_unbonded(&self) -> BalanceOf<T> {
291 let bonded_stake = self.bonded_stake();
292
293 let net_balance = self.ledger.effective_balance();
294
295 assert!(net_balance >= bonded_stake, "cannot be bonded with more than the agent balance");
296
297 net_balance.saturating_sub(bonded_stake)
298 }
299}