1use super::*;
20use frame_support::traits::{
21 tokens::{
22 Fortitude,
23 Preservation::{self, Preserve, Protect},
24 Provenance::{self, Minted},
25 },
26 AccountTouch,
27};
28
29impl<T: Config<I>, I: 'static> fungible::Inspect<T::AccountId> for Pallet<T, I> {
30 type Balance = T::Balance;
31
32 fn total_issuance() -> Self::Balance {
33 TotalIssuance::<T, I>::get()
34 }
35 fn active_issuance() -> Self::Balance {
36 TotalIssuance::<T, I>::get().saturating_sub(InactiveIssuance::<T, I>::get())
37 }
38 fn minimum_balance() -> Self::Balance {
39 T::ExistentialDeposit::get()
40 }
41 fn total_balance(who: &T::AccountId) -> Self::Balance {
42 Self::account(who).total()
43 }
44 fn balance(who: &T::AccountId) -> Self::Balance {
45 Self::account(who).free
46 }
47 fn reducible_balance(
48 who: &T::AccountId,
49 preservation: Preservation,
50 force: Fortitude,
51 ) -> Self::Balance {
52 let a = Self::account(who);
53 let mut untouchable = Zero::zero();
54 if force == Polite {
55 untouchable = a.frozen.saturating_sub(a.reserved);
58 }
59 if preservation == Preserve
61 || preservation == Protect && !a.free.is_zero() &&
63 frame_system::Pallet::<T>::providers(who) == 1
64 || preservation == Expendable && !a.free.is_zero() &&
66 !frame_system::Pallet::<T>::can_dec_provider(who)
67 {
68 untouchable = untouchable.max(T::ExistentialDeposit::get());
70 }
71 a.free.saturating_sub(untouchable)
73 }
74 fn can_deposit(
75 who: &T::AccountId,
76 amount: Self::Balance,
77 provenance: Provenance,
78 ) -> DepositConsequence {
79 if amount.is_zero() {
80 return DepositConsequence::Success
81 }
82
83 if provenance == Minted && TotalIssuance::<T, I>::get().checked_add(&amount).is_none() {
84 return DepositConsequence::Overflow
85 }
86
87 let account = Self::account(who);
88 let new_free = match account.free.checked_add(&amount) {
89 None => return DepositConsequence::Overflow,
90 Some(x) if x < T::ExistentialDeposit::get() => return DepositConsequence::BelowMinimum,
91 Some(x) => x,
92 };
93
94 match account.reserved.checked_add(&new_free) {
95 Some(_) => {},
96 None => return DepositConsequence::Overflow,
97 };
98
99 DepositConsequence::Success
103 }
104 fn can_withdraw(
105 who: &T::AccountId,
106 amount: Self::Balance,
107 ) -> WithdrawConsequence<Self::Balance> {
108 if amount.is_zero() {
109 return WithdrawConsequence::Success
110 }
111
112 if TotalIssuance::<T, I>::get().checked_sub(&amount).is_none() {
113 return WithdrawConsequence::Underflow
114 }
115
116 let account = Self::account(who);
117 let new_free_balance = match account.free.checked_sub(&amount) {
118 Some(x) => x,
119 None => return WithdrawConsequence::BalanceLow,
120 };
121
122 let liquid = Self::reducible_balance(who, Expendable, Polite);
123 if amount > liquid {
124 return WithdrawConsequence::Frozen
125 }
126
127 let ed = T::ExistentialDeposit::get();
132 let success = if new_free_balance < ed {
133 if frame_system::Pallet::<T>::can_dec_provider(who) {
134 WithdrawConsequence::ReducedToZero(new_free_balance)
135 } else {
136 return WithdrawConsequence::WouldDie
137 }
138 } else {
139 WithdrawConsequence::Success
140 };
141
142 let new_total_balance = new_free_balance.saturating_add(account.reserved);
143
144 if new_total_balance < account.frozen {
146 return WithdrawConsequence::Frozen
147 }
148
149 success
150 }
151}
152
153impl<T: Config<I>, I: 'static> fungible::Unbalanced<T::AccountId> for Pallet<T, I> {
154 fn handle_dust(dust: fungible::Dust<T::AccountId, Self>) {
155 T::DustRemoval::on_unbalanced(dust.into_credit());
156 }
157 fn write_balance(
158 who: &T::AccountId,
159 amount: Self::Balance,
160 ) -> Result<Option<Self::Balance>, DispatchError> {
161 let max_reduction =
162 <Self as fungible::Inspect<_>>::reducible_balance(who, Expendable, Force);
163 let (result, maybe_dust) = Self::mutate_account(who, false, |account| -> DispatchResult {
164 let reduction = account.free.saturating_sub(amount);
166 ensure!(reduction <= max_reduction, Error::<T, I>::InsufficientBalance);
167
168 account.free = amount;
169 Ok(())
170 })?;
171 result?;
172 Ok(maybe_dust)
173 }
174
175 fn set_total_issuance(amount: Self::Balance) {
176 TotalIssuance::<T, I>::mutate(|t| *t = amount);
177 }
178
179 fn deactivate(amount: Self::Balance) {
180 InactiveIssuance::<T, I>::mutate(|b| {
181 *b = b.saturating_add(amount).min(TotalIssuance::<T, I>::get());
183 });
184 }
185
186 fn reactivate(amount: Self::Balance) {
187 InactiveIssuance::<T, I>::mutate(|b| b.saturating_reduce(amount));
188 }
189}
190
191impl<T: Config<I>, I: 'static> fungible::Mutate<T::AccountId> for Pallet<T, I> {
192 fn done_mint_into(who: &T::AccountId, amount: Self::Balance) {
193 Self::deposit_event(Event::<T, I>::Minted { who: who.clone(), amount });
194 }
195 fn done_burn_from(who: &T::AccountId, amount: Self::Balance) {
196 Self::deposit_event(Event::<T, I>::Burned { who: who.clone(), amount });
197 }
198 fn done_shelve(who: &T::AccountId, amount: Self::Balance) {
199 Self::deposit_event(Event::<T, I>::Suspended { who: who.clone(), amount });
200 }
201 fn done_restore(who: &T::AccountId, amount: Self::Balance) {
202 Self::deposit_event(Event::<T, I>::Restored { who: who.clone(), amount });
203 }
204 fn done_transfer(source: &T::AccountId, dest: &T::AccountId, amount: Self::Balance) {
205 Self::deposit_event(Event::<T, I>::Transfer {
206 from: source.clone(),
207 to: dest.clone(),
208 amount,
209 });
210 }
211}
212
213impl<T: Config<I>, I: 'static> fungible::MutateHold<T::AccountId> for Pallet<T, I> {
214 fn done_hold(reason: &Self::Reason, who: &T::AccountId, amount: Self::Balance) {
215 Self::deposit_event(Event::<T, I>::Held { reason: *reason, who: who.clone(), amount });
216 }
217 fn done_release(reason: &Self::Reason, who: &T::AccountId, amount: Self::Balance) {
218 Self::deposit_event(Event::<T, I>::Released { reason: *reason, who: who.clone(), amount });
219 }
220 fn done_burn_held(reason: &Self::Reason, who: &T::AccountId, amount: Self::Balance) {
221 Self::deposit_event(Event::<T, I>::BurnedHeld {
222 reason: *reason,
223 who: who.clone(),
224 amount,
225 });
226 }
227 fn done_transfer_on_hold(
228 reason: &Self::Reason,
229 source: &T::AccountId,
230 dest: &T::AccountId,
231 amount: Self::Balance,
232 ) {
233 Self::deposit_event(Event::<T, I>::TransferOnHold {
235 reason: *reason,
236 source: source.clone(),
237 dest: dest.clone(),
238 amount,
239 });
240 }
241 fn done_transfer_and_hold(
242 reason: &Self::Reason,
243 source: &T::AccountId,
244 dest: &T::AccountId,
245 transferred: Self::Balance,
246 ) {
247 Self::deposit_event(Event::<T, I>::TransferAndHold {
248 reason: *reason,
249 source: source.clone(),
250 dest: dest.clone(),
251 transferred,
252 })
253 }
254}
255
256impl<T: Config<I>, I: 'static> fungible::InspectHold<T::AccountId> for Pallet<T, I> {
257 type Reason = T::RuntimeHoldReason;
258
259 fn total_balance_on_hold(who: &T::AccountId) -> T::Balance {
260 Self::account(who).reserved
261 }
262 fn reducible_total_balance_on_hold(who: &T::AccountId, force: Fortitude) -> Self::Balance {
263 let a = Self::account(who);
265 let unavailable = if force == Force {
266 Self::Balance::zero()
267 } else {
268 a.frozen.saturating_sub(a.free)
271 };
272 a.reserved.saturating_sub(unavailable)
273 }
274 fn balance_on_hold(reason: &Self::Reason, who: &T::AccountId) -> T::Balance {
275 Holds::<T, I>::get(who)
276 .iter()
277 .find(|x| &x.id == reason)
278 .map_or_else(Zero::zero, |x| x.amount)
279 }
280 fn hold_available(reason: &Self::Reason, who: &T::AccountId) -> bool {
281 if frame_system::Pallet::<T>::providers(who) == 0 {
282 return false
283 }
284 let holds = Holds::<T, I>::get(who);
285 if holds.is_full() && !holds.iter().any(|x| &x.id == reason) {
286 return false
287 }
288 true
289 }
290}
291
292impl<T: Config<I>, I: 'static> fungible::UnbalancedHold<T::AccountId> for Pallet<T, I> {
293 fn set_balance_on_hold(
294 reason: &Self::Reason,
295 who: &T::AccountId,
296 amount: Self::Balance,
297 ) -> DispatchResult {
298 let mut new_account = Self::account(who);
299 let mut holds = Holds::<T, I>::get(who);
300 let mut increase = true;
301 let mut delta = amount;
302
303 if let Some(item) = holds.iter_mut().find(|x| &x.id == reason) {
304 delta = item.amount.max(amount) - item.amount.min(amount);
305 increase = amount > item.amount;
306 item.amount = amount;
307 holds.retain(|x| !x.amount.is_zero());
308 } else {
309 if !amount.is_zero() {
310 holds
311 .try_push(IdAmount { id: *reason, amount })
312 .map_err(|_| Error::<T, I>::TooManyHolds)?;
313 }
314 }
315
316 new_account.reserved = if increase {
317 new_account.reserved.checked_add(&delta).ok_or(ArithmeticError::Overflow)?
318 } else {
319 new_account.reserved.checked_sub(&delta).ok_or(ArithmeticError::Underflow)?
320 };
321
322 let (result, maybe_dust) =
323 Self::try_mutate_account(who, false, |a, _| -> DispatchResult {
324 *a = new_account;
325 Ok(())
326 })?;
327 debug_assert!(
328 maybe_dust.is_none(),
329 "Does not alter main balance; dust only happens when it is altered; qed"
330 );
331 Holds::<T, I>::insert(who, holds);
332 Ok(result)
333 }
334}
335
336impl<T: Config<I>, I: 'static> fungible::InspectFreeze<T::AccountId> for Pallet<T, I> {
337 type Id = T::FreezeIdentifier;
338
339 fn balance_frozen(id: &Self::Id, who: &T::AccountId) -> Self::Balance {
340 let locks = Freezes::<T, I>::get(who);
341 locks.into_iter().find(|l| &l.id == id).map_or(Zero::zero(), |l| l.amount)
342 }
343
344 fn can_freeze(id: &Self::Id, who: &T::AccountId) -> bool {
345 let l = Freezes::<T, I>::get(who);
346 !l.is_full() || l.iter().any(|x| &x.id == id)
347 }
348}
349
350impl<T: Config<I>, I: 'static> fungible::MutateFreeze<T::AccountId> for Pallet<T, I> {
351 fn set_freeze(id: &Self::Id, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
352 if amount.is_zero() {
353 return Self::thaw(id, who)
354 }
355 let mut locks = Freezes::<T, I>::get(who);
356 if let Some(i) = locks.iter_mut().find(|x| &x.id == id) {
357 i.amount = amount;
358 } else {
359 locks
360 .try_push(IdAmount { id: *id, amount })
361 .map_err(|_| Error::<T, I>::TooManyFreezes)?;
362 }
363 Self::update_freezes(who, locks.as_bounded_slice())
364 }
365
366 fn extend_freeze(id: &Self::Id, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
367 if amount.is_zero() {
368 return Ok(())
369 }
370 let mut locks = Freezes::<T, I>::get(who);
371 if let Some(i) = locks.iter_mut().find(|x| &x.id == id) {
372 i.amount = i.amount.max(amount);
373 } else {
374 locks
375 .try_push(IdAmount { id: *id, amount })
376 .map_err(|_| Error::<T, I>::TooManyFreezes)?;
377 }
378 Self::update_freezes(who, locks.as_bounded_slice())
379 }
380
381 fn thaw(id: &Self::Id, who: &T::AccountId) -> DispatchResult {
382 let mut locks = Freezes::<T, I>::get(who);
383 locks.retain(|l| &l.id != id);
384 Self::update_freezes(who, locks.as_bounded_slice())
385 }
386}
387
388impl<T: Config<I>, I: 'static> fungible::Balanced<T::AccountId> for Pallet<T, I> {
389 type OnDropCredit = NegativeImbalance<T, I>;
390 type OnDropDebt = PositiveImbalance<T, I>;
391
392 fn done_deposit(who: &T::AccountId, amount: Self::Balance) {
393 Self::deposit_event(Event::<T, I>::Deposit { who: who.clone(), amount });
394 }
395 fn done_withdraw(who: &T::AccountId, amount: Self::Balance) {
396 Self::deposit_event(Event::<T, I>::Withdraw { who: who.clone(), amount });
397 }
398 fn done_issue(amount: Self::Balance) {
399 if !amount.is_zero() {
400 Self::deposit_event(Event::<T, I>::Issued { amount });
401 }
402 }
403 fn done_rescind(amount: Self::Balance) {
404 Self::deposit_event(Event::<T, I>::Rescinded { amount });
405 }
406}
407
408impl<T: Config<I>, I: 'static> fungible::BalancedHold<T::AccountId> for Pallet<T, I> {}
409
410impl<T: Config<I>, I: 'static>
411 fungible::hold::DoneSlash<T::RuntimeHoldReason, T::AccountId, T::Balance> for Pallet<T, I>
412{
413 fn done_slash(reason: &T::RuntimeHoldReason, who: &T::AccountId, amount: T::Balance) {
414 T::DoneSlashHandler::done_slash(reason, who, amount);
415 }
416}
417
418impl<T: Config<I>, I: 'static> AccountTouch<(), T::AccountId> for Pallet<T, I> {
419 type Balance = T::Balance;
420 fn deposit_required(_: ()) -> Self::Balance {
421 Self::Balance::zero()
422 }
423 fn should_touch(_: (), _: &T::AccountId) -> bool {
424 false
425 }
426 fn touch(_: (), _: &T::AccountId, _: &T::AccountId) -> DispatchResult {
427 Ok(())
428 }
429}