1
// This file is part of Substrate.
2

            
3
// Copyright (C) Parity Technologies (UK) Ltd.
4
// SPDX-License-Identifier: Apache-2.0
5

            
6
// Licensed under the Apache License, Version 2.0 (the "License");
7
// you may not use this file except in compliance with the License.
8
// You may obtain a copy of the License at
9
//
10
// 	http://www.apache.org/licenses/LICENSE-2.0
11
//
12
// Unless required by applicable law or agreed to in writing, software
13
// distributed under the License is distributed on an "AS IS" BASIS,
14
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
// See the License for the specific language governing permissions and
16
// limitations under the License.
17

            
18
//! Functions for the Assets pallet.
19

            
20
use super::*;
21
use alloc::vec;
22
use frame_support::{defensive, traits::Get, BoundedVec};
23

            
24
#[must_use]
25
pub(super) enum DeadConsequence {
26
	Remove,
27
	Keep,
28
}
29

            
30
use DeadConsequence::*;
31

            
32
// The main implementation block for the module.
33
impl<T: Config<I>, I: 'static> Pallet<T, I> {
34
	// Public immutables
35

            
36
	/// Return the extra "sid-car" data for `id`/`who`, or `None` if the account doesn't exist.
37
	pub fn adjust_extra(
38
		id: T::AssetId,
39
		who: impl core::borrow::Borrow<T::AccountId>,
40
	) -> Option<ExtraMutator<T, I>> {
41
		ExtraMutator::maybe_new(id, who)
42
	}
43

            
44
	/// Get the asset `id` balance of `who`, or zero if the asset-account doesn't exist.
45
	pub fn balance(id: T::AssetId, who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
46
		Self::maybe_balance(id, who).unwrap_or_default()
47
	}
48

            
49
	/// Get the asset `id` balance of `who` if the asset-account exists.
50
	pub fn maybe_balance(
51
		id: T::AssetId,
52
		who: impl core::borrow::Borrow<T::AccountId>,
53
	) -> Option<T::Balance> {
54
		Account::<T, I>::get(id, who.borrow()).map(|a| a.balance)
55
	}
56

            
57
	/// Get the total supply of an asset `id`.
58
	pub fn total_supply(id: T::AssetId) -> T::Balance {
59
		Self::maybe_total_supply(id).unwrap_or_default()
60
	}
61

            
62
	/// Get the total supply of an asset `id` if the asset exists.
63
	pub fn maybe_total_supply(id: T::AssetId) -> Option<T::Balance> {
64
		Asset::<T, I>::get(id).map(|x| x.supply)
65
	}
66

            
67
	pub(super) fn new_account(
68
		who: &T::AccountId,
69
		d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
70
		maybe_deposit: Option<(&T::AccountId, DepositBalanceOf<T, I>)>,
71
	) -> Result<ExistenceReasonOf<T, I>, DispatchError> {
72
		let accounts = d.accounts.checked_add(1).ok_or(ArithmeticError::Overflow)?;
73
		let reason = if let Some((depositor, deposit)) = maybe_deposit {
74
			if depositor == who {
75
				ExistenceReason::DepositHeld(deposit)
76
			} else {
77
				ExistenceReason::DepositFrom(depositor.clone(), deposit)
78
			}
79
		} else if d.is_sufficient {
80
			frame_system::Pallet::<T>::inc_sufficients(who);
81
			d.sufficients.saturating_inc();
82
			ExistenceReason::Sufficient
83
		} else {
84
			frame_system::Pallet::<T>::inc_consumers(who)
85
				.map_err(|_| Error::<T, I>::UnavailableConsumer)?;
86
			// We ensure that we can still increment consumers once more because we could otherwise
87
			// allow accidental usage of all consumer references which could cause grief.
88
			if !frame_system::Pallet::<T>::can_inc_consumer(who) {
89
				frame_system::Pallet::<T>::dec_consumers(who);
90
				return Err(Error::<T, I>::UnavailableConsumer.into())
91
			}
92
			ExistenceReason::Consumer
93
		};
94
		d.accounts = accounts;
95
		Ok(reason)
96
	}
97

            
98
	pub(super) fn dead_account(
99
		who: &T::AccountId,
100
		d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
101
		reason: &ExistenceReasonOf<T, I>,
102
		force: bool,
103
	) -> DeadConsequence {
104
		use ExistenceReason::*;
105
		match *reason {
106
			Consumer => frame_system::Pallet::<T>::dec_consumers(who),
107
			Sufficient => {
108
				d.sufficients = d.sufficients.saturating_sub(1);
109
				frame_system::Pallet::<T>::dec_sufficients(who);
110
			},
111
			DepositRefunded => {},
112
			DepositHeld(_) | DepositFrom(..) if !force => return Keep,
113
			DepositHeld(_) | DepositFrom(..) => {},
114
		}
115
		d.accounts = d.accounts.saturating_sub(1);
116
		Remove
117
	}
118

            
119
	/// Returns `true` when the balance of `account` can be increased by `amount`.
120
	///
121
	/// - `id`: The id of the asset that should be increased.
122
	/// - `who`: The account of which the balance should be increased.
123
	/// - `amount`: The amount by which the balance should be increased.
124
	/// - `increase_supply`: Will the supply of the asset be increased by `amount` at the same time
125
	///   as crediting the `account`.
126
	pub(super) fn can_increase(
127
		id: T::AssetId,
128
		who: &T::AccountId,
129
		amount: T::Balance,
130
		increase_supply: bool,
131
	) -> DepositConsequence {
132
		let details = match Asset::<T, I>::get(&id) {
133
			Some(details) => details,
134
			None => return DepositConsequence::UnknownAsset,
135
		};
136
		if details.status == AssetStatus::Destroying {
137
			return DepositConsequence::UnknownAsset
138
		}
139
		if increase_supply && details.supply.checked_add(&amount).is_none() {
140
			return DepositConsequence::Overflow
141
		}
142
		if let Some(account) = Account::<T, I>::get(id, who) {
143
			if account.status.is_blocked() {
144
				return DepositConsequence::Blocked
145
			}
146
			if account.balance.checked_add(&amount).is_none() {
147
				return DepositConsequence::Overflow
148
			}
149
		} else {
150
			if amount < details.min_balance {
151
				return DepositConsequence::BelowMinimum
152
			}
153
			if !details.is_sufficient && !frame_system::Pallet::<T>::can_accrue_consumers(who, 2) {
154
				return DepositConsequence::CannotCreate
155
			}
156
			if details.is_sufficient && details.sufficients.checked_add(1).is_none() {
157
				return DepositConsequence::Overflow
158
			}
159
		}
160

            
161
		DepositConsequence::Success
162
	}
163

            
164
	/// Return the consequence of a withdraw.
165
	pub(super) fn can_decrease(
166
		id: T::AssetId,
167
		who: &T::AccountId,
168
		amount: T::Balance,
169
		keep_alive: bool,
170
	) -> WithdrawConsequence<T::Balance> {
171
		use WithdrawConsequence::*;
172
		let details = match Asset::<T, I>::get(&id) {
173
			Some(details) => details,
174
			None => return UnknownAsset,
175
		};
176
		if details.supply.checked_sub(&amount).is_none() {
177
			return Underflow
178
		}
179
		if details.status == AssetStatus::Frozen {
180
			return Frozen
181
		}
182
		if details.status == AssetStatus::Destroying {
183
			return UnknownAsset
184
		}
185
		if amount.is_zero() {
186
			return Success
187
		}
188
		let account = match Account::<T, I>::get(&id, who) {
189
			Some(a) => a,
190
			None => return BalanceLow,
191
		};
192
		if account.status.is_frozen() {
193
			return Frozen
194
		}
195
		if let Some(rest) = account.balance.checked_sub(&amount) {
196
			if let Some(frozen) = T::Freezer::frozen_balance(id.clone(), who) {
197
				match frozen.checked_add(&details.min_balance) {
198
					Some(required) if rest < required => return Frozen,
199
					None => return Overflow,
200
					_ => {},
201
				}
202
			}
203

            
204
			if rest < details.min_balance {
205
				if keep_alive {
206
					WouldDie
207
				} else {
208
					ReducedToZero(rest)
209
				}
210
			} else {
211
				Success
212
			}
213
		} else {
214
			BalanceLow
215
		}
216
	}
217

            
218
	// Maximum `amount` that can be passed into `can_withdraw` to result in a `WithdrawConsequence`
219
	// of `Success`.
220
	pub(super) fn reducible_balance(
221
		id: T::AssetId,
222
		who: &T::AccountId,
223
		keep_alive: bool,
224
	) -> Result<T::Balance, DispatchError> {
225
		let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
226
		ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
227

            
228
		let account = Account::<T, I>::get(&id, who).ok_or(Error::<T, I>::NoAccount)?;
229
		ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen);
230

            
231
		let amount = if let Some(frozen) = T::Freezer::frozen_balance(id, who) {
232
			// Frozen balance: account CANNOT be deleted
233
			let required =
234
				frozen.checked_add(&details.min_balance).ok_or(ArithmeticError::Overflow)?;
235
			account.balance.saturating_sub(required)
236
		} else {
237
			if keep_alive {
238
				// We want to keep the account around.
239
				account.balance.saturating_sub(details.min_balance)
240
			} else {
241
				// Don't care if the account dies
242
				account.balance
243
			}
244
		};
245
		Ok(amount.min(details.supply))
246
	}
247

            
248
	/// Make preparatory checks for debiting some funds from an account. Flags indicate requirements
249
	/// of the debit.
250
	///
251
	/// - `amount`: The amount desired to be debited. The actual amount returned for debit may be
252
	///   less (in the case of `best_effort` being `true`) or greater by up to the minimum balance
253
	///   less one.
254
	/// - `keep_alive`: Require that `target` must stay alive.
255
	/// - `respect_freezer`: Respect any freezes on the account or token (or not).
256
	/// - `best_effort`: The debit amount may be less than `amount`.
257
	///
258
	/// On success, the amount which should be debited (this will always be at least `amount` unless
259
	/// `best_effort` is `true`) together with an optional value indicating the argument which must
260
	/// be passed into the `melted` function of the `T::Freezer` if `Some`.
261
	///
262
	/// If no valid debit can be made then return an `Err`.
263
	pub(super) fn prep_debit(
264
		id: T::AssetId,
265
		target: &T::AccountId,
266
		amount: T::Balance,
267
		f: DebitFlags,
268
	) -> Result<T::Balance, DispatchError> {
269
		let actual = Self::reducible_balance(id.clone(), target, f.keep_alive)?.min(amount);
270
		ensure!(f.best_effort || actual >= amount, Error::<T, I>::BalanceLow);
271

            
272
		let conseq = Self::can_decrease(id, target, actual, f.keep_alive);
273
		let actual = match conseq.into_result(f.keep_alive) {
274
			Ok(dust) => actual.saturating_add(dust), //< guaranteed by reducible_balance
275
			Err(e) => {
276
				debug_assert!(false, "passed from reducible_balance; qed");
277
				return Err(e)
278
			},
279
		};
280

            
281
		Ok(actual)
282
	}
283

            
284
	/// Make preparatory checks for crediting some funds from an account. Flags indicate
285
	/// requirements of the credit.
286
	///
287
	/// - `amount`: The amount desired to be credited.
288
	/// - `debit`: The amount by which some other account has been debited. If this is greater than
289
	///   `amount`, then the `burn_dust` parameter takes effect.
290
	/// - `burn_dust`: Indicates that in the case of debit being greater than amount, the additional
291
	///   (dust) value should be burned, rather than credited.
292
	///
293
	/// On success, the amount which should be credited (this will always be at least `amount`)
294
	/// together with an optional value indicating the value which should be burned. The latter
295
	/// will always be `None` as long as `burn_dust` is `false` or `debit` is no greater than
296
	/// `amount`.
297
	///
298
	/// If no valid credit can be made then return an `Err`.
299
	pub(super) fn prep_credit(
300
		id: T::AssetId,
301
		dest: &T::AccountId,
302
		amount: T::Balance,
303
		debit: T::Balance,
304
		burn_dust: bool,
305
	) -> Result<(T::Balance, Option<T::Balance>), DispatchError> {
306
		let (credit, maybe_burn) = match (burn_dust, debit.checked_sub(&amount)) {
307
			(true, Some(dust)) => (amount, Some(dust)),
308
			_ => (debit, None),
309
		};
310
		Self::can_increase(id, dest, credit, false).into_result()?;
311
		Ok((credit, maybe_burn))
312
	}
313

            
314
	/// Creates an account for `who` to hold asset `id` with a zero balance and takes a deposit.
315
	///
316
	/// When `check_depositor` is set to true, the depositor must be either the asset's Admin or
317
	/// Freezer, otherwise the depositor can be any account.
318
	pub(super) fn do_touch(
319
		id: T::AssetId,
320
		who: T::AccountId,
321
		depositor: T::AccountId,
322
		check_depositor: bool,
323
	) -> DispatchResult {
324
		ensure!(!Account::<T, I>::contains_key(&id, &who), Error::<T, I>::AlreadyExists);
325
		let deposit = T::AssetAccountDeposit::get();
326
		let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
327
		ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
328
		ensure!(
329
			!check_depositor || &depositor == &details.admin || &depositor == &details.freezer,
330
			Error::<T, I>::NoPermission
331
		);
332
		let reason = Self::new_account(&who, &mut details, Some((&depositor, deposit)))?;
333
		T::Currency::reserve(&depositor, deposit)?;
334
		Asset::<T, I>::insert(&id, details);
335
		Account::<T, I>::insert(
336
			&id,
337
			&who,
338
			AssetAccountOf::<T, I> {
339
				balance: Zero::zero(),
340
				status: AccountStatus::Liquid,
341
				reason,
342
				extra: T::Extra::default(),
343
			},
344
		);
345
		Self::deposit_event(Event::Touched { asset_id: id, who, depositor });
346
		Ok(())
347
	}
348

            
349
	/// Returns a deposit or a consumer reference, destroying an asset-account.
350
	/// Non-zero balance accounts refunded and destroyed only if `allow_burn` is true.
351
	pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult {
352
		use AssetStatus::*;
353
		use ExistenceReason::*;
354
		let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
355
		ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::<T, I>::NoDeposit);
356
		let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
357
		ensure!(matches!(details.status, Live | Frozen), Error::<T, I>::IncorrectStatus);
358
		ensure!(account.balance.is_zero() || allow_burn, Error::<T, I>::WouldBurn);
359

            
360
		if let Some(deposit) = account.reason.take_deposit() {
361
			T::Currency::unreserve(&who, deposit);
362
		}
363

            
364
		if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) {
365
			Account::<T, I>::remove(&id, &who);
366
		} else {
367
			debug_assert!(false, "refund did not result in dead account?!");
368
			// deposit may have been refunded, need to update `Account`
369
			Account::<T, I>::insert(id, &who, account);
370
			return Ok(())
371
		}
372
		Asset::<T, I>::insert(&id, details);
373
		// Executing a hook here is safe, since it is not in a `mutate`.
374
		T::Freezer::died(id, &who);
375
		Ok(())
376
	}
377

            
378
	/// Refunds the `DepositFrom` of an account only if its balance is zero.
379
	///
380
	/// If the `maybe_check_caller` parameter is specified, it must match the account that provided
381
	/// the deposit or must be the admin of the asset.
382
	pub(super) fn do_refund_other(
383
		id: T::AssetId,
384
		who: &T::AccountId,
385
		maybe_check_caller: Option<T::AccountId>,
386
	) -> DispatchResult {
387
		let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
388
		let (depositor, deposit) =
389
			account.reason.take_deposit_from().ok_or(Error::<T, I>::NoDeposit)?;
390
		let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
391
		ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
392
		ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen);
393
		if let Some(caller) = maybe_check_caller {
394
			ensure!(caller == depositor || caller == details.admin, Error::<T, I>::NoPermission);
395
		}
396
		ensure!(account.balance.is_zero(), Error::<T, I>::WouldBurn);
397

            
398
		T::Currency::unreserve(&depositor, deposit);
399

            
400
		if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) {
401
			Account::<T, I>::remove(&id, &who);
402
		} else {
403
			debug_assert!(false, "refund did not result in dead account?!");
404
			// deposit may have been refunded, need to update `Account`
405
			Account::<T, I>::insert(&id, &who, account);
406
			return Ok(())
407
		}
408
		Asset::<T, I>::insert(&id, details);
409
		// Executing a hook here is safe, since it is not in a `mutate`.
410
		T::Freezer::died(id, &who);
411
		return Ok(())
412
	}
413

            
414
	/// Increases the asset `id` balance of `beneficiary` by `amount`.
415
	///
416
	/// This alters the registered supply of the asset and emits an event.
417
	///
418
	/// Will return an error or will increase the amount by exactly `amount`.
419
	pub(super) fn do_mint(
420
		id: T::AssetId,
421
		beneficiary: &T::AccountId,
422
		amount: T::Balance,
423
		maybe_check_issuer: Option<T::AccountId>,
424
	) -> DispatchResult {
425
		Self::increase_balance(id.clone(), beneficiary, amount, |details| -> DispatchResult {
426
			if let Some(check_issuer) = maybe_check_issuer {
427
				ensure!(check_issuer == details.issuer, Error::<T, I>::NoPermission);
428
			}
429
			debug_assert!(details.supply.checked_add(&amount).is_some(), "checked in prep; qed");
430

            
431
			details.supply = details.supply.saturating_add(amount);
432

            
433
			Ok(())
434
		})?;
435

            
436
		Self::deposit_event(Event::Issued { asset_id: id, owner: beneficiary.clone(), amount });
437

            
438
		Ok(())
439
	}
440

            
441
	/// Increases the asset `id` balance of `beneficiary` by `amount`.
442
	///
443
	/// LOW-LEVEL: Does not alter the supply of asset or emit an event. Use `do_mint` if you need
444
	/// that. This is not intended to be used alone.
445
	///
446
	/// Will return an error or will increase the amount by exactly `amount`.
447
	pub(super) fn increase_balance(
448
		id: T::AssetId,
449
		beneficiary: &T::AccountId,
450
		amount: T::Balance,
451
		check: impl FnOnce(
452
			&mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
453
		) -> DispatchResult,
454
	) -> DispatchResult {
455
		if amount.is_zero() {
456
			return Ok(())
457
		}
458

            
459
		Self::can_increase(id.clone(), beneficiary, amount, true).into_result()?;
460
		Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
461
			let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
462
			ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
463
			check(details)?;
464

            
465
			Account::<T, I>::try_mutate(&id, beneficiary, |maybe_account| -> DispatchResult {
466
				match maybe_account {
467
					Some(ref mut account) => {
468
						account.balance.saturating_accrue(amount);
469
					},
470
					maybe_account @ None => {
471
						// Note this should never fail as it's already checked by
472
						// `can_increase`.
473
						ensure!(amount >= details.min_balance, TokenError::BelowMinimum);
474
						*maybe_account = Some(AssetAccountOf::<T, I> {
475
							balance: amount,
476
							reason: Self::new_account(beneficiary, details, None)?,
477
							status: AccountStatus::Liquid,
478
							extra: T::Extra::default(),
479
						});
480
					},
481
				}
482
				Ok(())
483
			})?;
484
			Ok(())
485
		})?;
486
		Ok(())
487
	}
488

            
489
	/// Reduces asset `id` balance of `target` by `amount`. Flags `f` can be given to alter whether
490
	/// it attempts a `best_effort` or makes sure to `keep_alive` the account.
491
	///
492
	/// This alters the registered supply of the asset and emits an event.
493
	///
494
	/// Will return an error and do nothing or will decrease the amount and return the amount
495
	/// reduced by.
496
	pub(super) fn do_burn(
497
		id: T::AssetId,
498
		target: &T::AccountId,
499
		amount: T::Balance,
500
		maybe_check_admin: Option<T::AccountId>,
501
		f: DebitFlags,
502
	) -> Result<T::Balance, DispatchError> {
503
		let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
504
		ensure!(
505
			d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
506
			Error::<T, I>::IncorrectStatus
507
		);
508

            
509
		let actual = Self::decrease_balance(id.clone(), target, amount, f, |actual, details| {
510
			// Check admin rights.
511
			if let Some(check_admin) = maybe_check_admin {
512
				ensure!(check_admin == details.admin, Error::<T, I>::NoPermission);
513
			}
514

            
515
			debug_assert!(details.supply >= actual, "checked in prep; qed");
516
			details.supply = details.supply.saturating_sub(actual);
517

            
518
			Ok(())
519
		})?;
520
		Self::deposit_event(Event::Burned { asset_id: id, owner: target.clone(), balance: actual });
521
		Ok(actual)
522
	}
523

            
524
	/// Reduces asset `id` balance of `target` by `amount`. Flags `f` can be given to alter whether
525
	/// it attempts a `best_effort` or makes sure to `keep_alive` the account.
526
	///
527
	/// LOW-LEVEL: Does not alter the supply of asset or emit an event. Use `do_burn` if you need
528
	/// that. This is not intended to be used alone.
529
	///
530
	/// Will return an error and do nothing or will decrease the amount and return the amount
531
	/// reduced by.
532
	pub(super) fn decrease_balance(
533
		id: T::AssetId,
534
		target: &T::AccountId,
535
		amount: T::Balance,
536
		f: DebitFlags,
537
		check: impl FnOnce(
538
			T::Balance,
539
			&mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
540
		) -> DispatchResult,
541
	) -> Result<T::Balance, DispatchError> {
542
		if amount.is_zero() {
543
			return Ok(amount)
544
		}
545

            
546
		let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
547
		ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
548

            
549
		let actual = Self::prep_debit(id.clone(), target, amount, f)?;
550
		let mut target_died: Option<DeadConsequence> = None;
551

            
552
		Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
553
			let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
554
			check(actual, details)?;
555

            
556
			Account::<T, I>::try_mutate(&id, target, |maybe_account| -> DispatchResult {
557
				let mut account = maybe_account.take().ok_or(Error::<T, I>::NoAccount)?;
558
				debug_assert!(account.balance >= actual, "checked in prep; qed");
559

            
560
				// Make the debit.
561
				account.balance = account.balance.saturating_sub(actual);
562
				if account.balance < details.min_balance {
563
					debug_assert!(account.balance.is_zero(), "checked in prep; qed");
564
					target_died = Some(Self::dead_account(target, details, &account.reason, false));
565
					if let Some(Remove) = target_died {
566
						return Ok(())
567
					}
568
				};
569
				*maybe_account = Some(account);
570
				Ok(())
571
			})?;
572

            
573
			Ok(())
574
		})?;
575

            
576
		// Execute hook outside of `mutate`.
577
		if let Some(Remove) = target_died {
578
			T::Freezer::died(id, target);
579
		}
580
		Ok(actual)
581
	}
582

            
583
	/// Reduces the asset `id` balance of `source` by some `amount` and increases the balance of
584
	/// `dest` by (similar) amount.
585
	///
586
	/// Returns the actual amount placed into `dest`. Exact semantics are determined by the flags
587
	/// `f`.
588
	///
589
	/// Will fail if the amount transferred is so small that it cannot create the destination due
590
	/// to minimum balance requirements.
591
	pub(super) fn do_transfer(
592
		id: T::AssetId,
593
		source: &T::AccountId,
594
		dest: &T::AccountId,
595
		amount: T::Balance,
596
		maybe_need_admin: Option<T::AccountId>,
597
		f: TransferFlags,
598
	) -> Result<T::Balance, DispatchError> {
599
		let (balance, died) =
600
			Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f)?;
601
		if let Some(Remove) = died {
602
			T::Freezer::died(id, source);
603
		}
604
		Ok(balance)
605
	}
606

            
607
	/// Same as `do_transfer` but it does not execute the `FrozenBalance::died` hook and
608
	/// instead returns whether and how the `source` account died in this operation.
609
	fn transfer_and_die(
610
		id: T::AssetId,
611
		source: &T::AccountId,
612
		dest: &T::AccountId,
613
		amount: T::Balance,
614
		maybe_need_admin: Option<T::AccountId>,
615
		f: TransferFlags,
616
	) -> Result<(T::Balance, Option<DeadConsequence>), DispatchError> {
617
		// Early exit if no-op.
618
		if amount.is_zero() {
619
			return Ok((amount, None))
620
		}
621
		let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
622
		ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
623

            
624
		// Figure out the debit and credit, together with side-effects.
625
		let debit = Self::prep_debit(id.clone(), source, amount, f.into())?;
626
		let (credit, maybe_burn) = Self::prep_credit(id.clone(), dest, amount, debit, f.burn_dust)?;
627

            
628
		let mut source_account =
629
			Account::<T, I>::get(&id, &source).ok_or(Error::<T, I>::NoAccount)?;
630
		let mut source_died: Option<DeadConsequence> = None;
631

            
632
		Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
633
			let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
634

            
635
			// Check admin rights.
636
			if let Some(need_admin) = maybe_need_admin {
637
				ensure!(need_admin == details.admin, Error::<T, I>::NoPermission);
638
			}
639

            
640
			// Skip if source == dest
641
			if source == dest {
642
				return Ok(())
643
			}
644

            
645
			// Burn any dust if needed.
646
			if let Some(burn) = maybe_burn {
647
				// Debit dust from supply; this will not saturate since it's already checked in
648
				// prep.
649
				debug_assert!(details.supply >= burn, "checked in prep; qed");
650
				details.supply = details.supply.saturating_sub(burn);
651
			}
652

            
653
			// Debit balance from source; this will not saturate since it's already checked in prep.
654
			debug_assert!(source_account.balance >= debit, "checked in prep; qed");
655
			source_account.balance = source_account.balance.saturating_sub(debit);
656

            
657
			Account::<T, I>::try_mutate(&id, &dest, |maybe_account| -> DispatchResult {
658
				match maybe_account {
659
					Some(ref mut account) => {
660
						// Calculate new balance; this will not saturate since it's already checked
661
						// in prep.
662
						debug_assert!(
663
							account.balance.checked_add(&credit).is_some(),
664
							"checked in prep; qed"
665
						);
666
						account.balance.saturating_accrue(credit);
667
					},
668
					maybe_account @ None => {
669
						*maybe_account = Some(AssetAccountOf::<T, I> {
670
							balance: credit,
671
							status: AccountStatus::Liquid,
672
							reason: Self::new_account(dest, details, None)?,
673
							extra: T::Extra::default(),
674
						});
675
					},
676
				}
677
				Ok(())
678
			})?;
679

            
680
			// Remove source account if it's now dead.
681
			if source_account.balance < details.min_balance {
682
				debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
683
				source_died =
684
					Some(Self::dead_account(source, details, &source_account.reason, false));
685
				if let Some(Remove) = source_died {
686
					Account::<T, I>::remove(&id, &source);
687
					return Ok(())
688
				}
689
			}
690
			Account::<T, I>::insert(&id, &source, &source_account);
691
			Ok(())
692
		})?;
693

            
694
		Self::deposit_event(Event::Transferred {
695
			asset_id: id,
696
			from: source.clone(),
697
			to: dest.clone(),
698
			amount: credit,
699
		});
700
		Ok((credit, source_died))
701
	}
702

            
703
	/// Create a new asset without taking a deposit.
704
	///
705
	/// * `id`: The `AssetId` you want the new asset to have. Must not already be in use.
706
	/// * `owner`: The owner, issuer, admin, and freezer of this asset upon creation.
707
	/// * `is_sufficient`: Whether this asset needs users to have an existential deposit to hold
708
	///   this asset.
709
	/// * `min_balance`: The minimum balance a user is allowed to have of this asset before they are
710
	///   considered dust and cleaned up.
711
	pub(super) fn do_force_create(
712
		id: T::AssetId,
713
		owner: T::AccountId,
714
		is_sufficient: bool,
715
		min_balance: T::Balance,
716
	) -> DispatchResult {
717
		ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
718
		ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
719
		if let Some(next_id) = NextAssetId::<T, I>::get() {
720
			ensure!(id == next_id, Error::<T, I>::BadAssetId);
721
		}
722

            
723
		Asset::<T, I>::insert(
724
			&id,
725
			AssetDetails {
726
				owner: owner.clone(),
727
				issuer: owner.clone(),
728
				admin: owner.clone(),
729
				freezer: owner.clone(),
730
				supply: Zero::zero(),
731
				deposit: Zero::zero(),
732
				min_balance,
733
				is_sufficient,
734
				accounts: 0,
735
				sufficients: 0,
736
				approvals: 0,
737
				status: AssetStatus::Live,
738
			},
739
		);
740
		ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
741
		Self::deposit_event(Event::ForceCreated { asset_id: id, owner: owner.clone() });
742
		Ok(())
743
	}
744

            
745
	/// Start the process of destroying an asset, by setting the asset status to `Destroying`, and
746
	/// emitting the `DestructionStarted` event.
747
	pub(super) fn do_start_destroy(
748
		id: T::AssetId,
749
		maybe_check_owner: Option<T::AccountId>,
750
	) -> DispatchResult {
751
		Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> {
752
			let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
753
			if let Some(check_owner) = maybe_check_owner {
754
				ensure!(details.owner == check_owner, Error::<T, I>::NoPermission);
755
			}
756
			details.status = AssetStatus::Destroying;
757

            
758
			Self::deposit_event(Event::DestructionStarted { asset_id: id });
759
			Ok(())
760
		})
761
	}
762

            
763
	/// Destroy accounts associated with a given asset up to the max (T::RemoveItemsLimit).
764
	///
765
	/// Each call emits the `Event::DestroyedAccounts` event.
766
	/// Returns the number of destroyed accounts.
767
	pub(super) fn do_destroy_accounts(
768
		id: T::AssetId,
769
		max_items: u32,
770
	) -> Result<u32, DispatchError> {
771
		let mut dead_accounts: Vec<T::AccountId> = vec![];
772
		let mut remaining_accounts = 0;
773
		let _ =
774
			Asset::<T, I>::try_mutate_exists(&id, |maybe_details| -> Result<(), DispatchError> {
775
				let mut details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
776
				// Should only destroy accounts while the asset is in a destroying state
777
				ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
778
				for (i, (who, mut v)) in Account::<T, I>::iter_prefix(&id).enumerate() {
779
					// unreserve the existence deposit if any
780
					if let Some((depositor, deposit)) = v.reason.take_deposit_from() {
781
						T::Currency::unreserve(&depositor, deposit);
782
					} else if let Some(deposit) = v.reason.take_deposit() {
783
						T::Currency::unreserve(&who, deposit);
784
					}
785
					if let Remove = Self::dead_account(&who, &mut details, &v.reason, false) {
786
						Account::<T, I>::remove(&id, &who);
787
						dead_accounts.push(who);
788
					} else {
789
						// deposit may have been released, need to update `Account`
790
						Account::<T, I>::insert(&id, &who, v);
791
						defensive!("destroy did not result in dead account?!");
792
					}
793
					if i + 1 >= (max_items as usize) {
794
						break
795
					}
796
				}
797
				remaining_accounts = details.accounts;
798
				Ok(())
799
			})?;
800

            
801
		for who in &dead_accounts {
802
			T::Freezer::died(id.clone(), &who);
803
		}
804

            
805
		Self::deposit_event(Event::AccountsDestroyed {
806
			asset_id: id,
807
			accounts_destroyed: dead_accounts.len() as u32,
808
			accounts_remaining: remaining_accounts as u32,
809
		});
810
		Ok(dead_accounts.len() as u32)
811
	}
812

            
813
	/// Destroy approvals associated with a given asset up to the max (T::RemoveItemsLimit).
814
	///
815
	/// Each call emits the `Event::DestroyedApprovals` event
816
	/// Returns the number of destroyed approvals.
817
	pub(super) fn do_destroy_approvals(
818
		id: T::AssetId,
819
		max_items: u32,
820
	) -> Result<u32, DispatchError> {
821
		let mut removed_approvals = 0;
822
		let _ = Asset::<T, I>::try_mutate_exists(
823
			id.clone(),
824
			|maybe_details| -> Result<(), DispatchError> {
825
				let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
826

            
827
				// Should only destroy accounts while the asset is in a destroying state.
828
				ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
829

            
830
				for ((owner, _), approval) in Approvals::<T, I>::drain_prefix((id.clone(),)) {
831
					T::Currency::unreserve(&owner, approval.deposit);
832
					removed_approvals = removed_approvals.saturating_add(1);
833
					details.approvals = details.approvals.saturating_sub(1);
834
					if removed_approvals >= max_items {
835
						break
836
					}
837
				}
838
				Self::deposit_event(Event::ApprovalsDestroyed {
839
					asset_id: id,
840
					approvals_destroyed: removed_approvals as u32,
841
					approvals_remaining: details.approvals as u32,
842
				});
843
				Ok(())
844
			},
845
		)?;
846
		Ok(removed_approvals)
847
	}
848

            
849
	/// Complete destroying an asset and unreserve the deposit.
850
	///
851
	/// On success, the `Event::Destroyed` event is emitted.
852
	pub(super) fn do_finish_destroy(id: T::AssetId) -> DispatchResult {
853
		Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> {
854
			let details = maybe_details.take().ok_or(Error::<T, I>::Unknown)?;
855
			ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
856
			ensure!(details.accounts == 0, Error::<T, I>::InUse);
857
			ensure!(details.approvals == 0, Error::<T, I>::InUse);
858
			ensure!(T::CallbackHandle::destroyed(&id).is_ok(), Error::<T, I>::CallbackFailed);
859

            
860
			let metadata = Metadata::<T, I>::take(&id);
861
			T::Currency::unreserve(
862
				&details.owner,
863
				details.deposit.saturating_add(metadata.deposit),
864
			);
865
			Self::deposit_event(Event::Destroyed { asset_id: id });
866

            
867
			Ok(())
868
		})
869
	}
870

            
871
	/// Creates an approval from `owner` to spend `amount` of asset `id` tokens by 'delegate'
872
	/// while reserving `T::ApprovalDeposit` from owner
873
	///
874
	/// If an approval already exists, the new amount is added to such existing approval
875
	pub(super) fn do_approve_transfer(
876
		id: T::AssetId,
877
		owner: &T::AccountId,
878
		delegate: &T::AccountId,
879
		amount: T::Balance,
880
	) -> DispatchResult {
881
		let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
882
		ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
883
		Approvals::<T, I>::try_mutate(
884
			(id.clone(), &owner, &delegate),
885
			|maybe_approved| -> DispatchResult {
886
				let mut approved = match maybe_approved.take() {
887
					// an approval already exists and is being updated
888
					Some(a) => a,
889
					// a new approval is created
890
					None => {
891
						d.approvals.saturating_inc();
892
						Default::default()
893
					},
894
				};
895
				let deposit_required = T::ApprovalDeposit::get();
896
				if approved.deposit < deposit_required {
897
					T::Currency::reserve(owner, deposit_required - approved.deposit)?;
898
					approved.deposit = deposit_required;
899
				}
900
				approved.amount = approved.amount.saturating_add(amount);
901
				*maybe_approved = Some(approved);
902
				Ok(())
903
			},
904
		)?;
905
		Asset::<T, I>::insert(&id, d);
906
		Self::deposit_event(Event::ApprovedTransfer {
907
			asset_id: id,
908
			source: owner.clone(),
909
			delegate: delegate.clone(),
910
			amount,
911
		});
912

            
913
		Ok(())
914
	}
915

            
916
	/// Reduces the asset `id` balance of `owner` by some `amount` and increases the balance of
917
	/// `dest` by (similar) amount, checking that 'delegate' has an existing approval from `owner`
918
	/// to spend`amount`.
919
	///
920
	/// Will fail if `amount` is greater than the approval from `owner` to 'delegate'
921
	/// Will unreserve the deposit from `owner` if the entire approved `amount` is spent by
922
	/// 'delegate'
923
	pub(super) fn do_transfer_approved(
924
		id: T::AssetId,
925
		owner: &T::AccountId,
926
		delegate: &T::AccountId,
927
		destination: &T::AccountId,
928
		amount: T::Balance,
929
	) -> DispatchResult {
930
		let mut owner_died: Option<DeadConsequence> = None;
931

            
932
		let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
933
		ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
934

            
935
		Approvals::<T, I>::try_mutate_exists(
936
			(id.clone(), &owner, delegate),
937
			|maybe_approved| -> DispatchResult {
938
				let mut approved = maybe_approved.take().ok_or(Error::<T, I>::Unapproved)?;
939
				let remaining =
940
					approved.amount.checked_sub(&amount).ok_or(Error::<T, I>::Unapproved)?;
941

            
942
				let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
943
				owner_died =
944
					Self::transfer_and_die(id.clone(), owner, destination, amount, None, f)?.1;
945

            
946
				if remaining.is_zero() {
947
					T::Currency::unreserve(owner, approved.deposit);
948
					Asset::<T, I>::mutate(id.clone(), |maybe_details| {
949
						if let Some(details) = maybe_details {
950
							details.approvals.saturating_dec();
951
						}
952
					});
953
				} else {
954
					approved.amount = remaining;
955
					*maybe_approved = Some(approved);
956
				}
957
				Ok(())
958
			},
959
		)?;
960

            
961
		// Execute hook outside of `mutate`.
962
		if let Some(Remove) = owner_died {
963
			T::Freezer::died(id, owner);
964
		}
965
		Ok(())
966
	}
967

            
968
	/// Do set metadata
969
	pub(super) fn do_set_metadata(
970
		id: T::AssetId,
971
		from: &T::AccountId,
972
		name: Vec<u8>,
973
		symbol: Vec<u8>,
974
		decimals: u8,
975
	) -> DispatchResult {
976
		let bounded_name: BoundedVec<u8, T::StringLimit> =
977
			name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
978
		let bounded_symbol: BoundedVec<u8, T::StringLimit> =
979
			symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
980

            
981
		let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
982
		ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
983
		ensure!(from == &d.owner, Error::<T, I>::NoPermission);
984

            
985
		Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
986
			ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::<T, I>::NoPermission);
987

            
988
			let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
989
			let new_deposit = Self::calc_metadata_deposit(&name, &symbol);
990

            
991
			if new_deposit > old_deposit {
992
				T::Currency::reserve(from, new_deposit - old_deposit)?;
993
			} else {
994
				T::Currency::unreserve(from, old_deposit - new_deposit);
995
			}
996

            
997
			*metadata = Some(AssetMetadata {
998
				deposit: new_deposit,
999
				name: bounded_name,
				symbol: bounded_symbol,
				decimals,
				is_frozen: false,
			});
			Self::deposit_event(Event::MetadataSet {
				asset_id: id,
				name,
				symbol,
				decimals,
				is_frozen: false,
			});
			Ok(())
		})
	}
	/// Calculate the metadata deposit for the provided data.
	pub(super) fn calc_metadata_deposit(name: &[u8], symbol: &[u8]) -> DepositBalanceOf<T, I> {
		T::MetadataDepositPerByte::get()
			.saturating_mul(((name.len() + symbol.len()) as u32).into())
			.saturating_add(T::MetadataDepositBase::get())
	}
	/// Returns all the non-zero balances for all assets of the given `account`.
	pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> {
		Asset::<T, I>::iter_keys()
			.filter_map(|id| {
				Self::maybe_balance(id.clone(), account.clone()).map(|balance| (id, balance))
			})
			.collect::<Vec<_>>()
	}
	/// Reset the team for the asset with the given `id`.
	///
	/// ### Parameters
	/// - `id`: The identifier of the asset for which the team is being reset.
	/// - `owner`: The new `owner` account for the asset.
	/// - `admin`: The new `admin` account for the asset.
	/// - `issuer`: The new `issuer` account for the asset.
	/// - `freezer`: The new `freezer` account for the asset.
	pub(crate) fn do_reset_team(
		id: T::AssetId,
		owner: T::AccountId,
		admin: T::AccountId,
		issuer: T::AccountId,
		freezer: T::AccountId,
	) -> DispatchResult {
		let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
		d.owner = owner;
		d.admin = admin;
		d.issuer = issuer;
		d.freezer = freezer;
		Asset::<T, I>::insert(&id, d);
		Ok(())
	}
}