Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Make pallet Assets instantiable #8483

Merged
3 commits merged into from
Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions frame/assets/src/extra_mutator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,23 @@ use super::*;
/// any uncommitted changes (see `commit` function) will be automatically committed to storage when
/// dropped. Changes, even after committed, may be reverted to their original values with the
/// `revert` function.
pub struct ExtraMutator<T: Config> {
pub struct ExtraMutator<T: Config<I>, I: 'static = ()> {
id: T::AssetId,
who: T::AccountId,
original: T::Extra,
pending: Option<T::Extra>,
}

impl<T: Config> Drop for ExtraMutator<T> {
impl<T: Config<I>, I: 'static> Drop for ExtraMutator<T, I> {
fn drop(&mut self) {
debug_assert!(self.commit().is_ok(), "attempt to write to non-existent asset account");
debug_assert!(
self.commit().is_ok(),
"attempt to write to non-existent asset account"
);
}
}

impl<T: Config> sp_std::ops::Deref for ExtraMutator<T> {
impl<T: Config<I>, I: 'static> sp_std::ops::Deref for ExtraMutator<T, I> {
type Target = T::Extra;
fn deref(&self) -> &T::Extra {
match self.pending {
Expand All @@ -48,7 +51,7 @@ impl<T: Config> sp_std::ops::Deref for ExtraMutator<T> {
}
}

impl<T: Config> sp_std::ops::DerefMut for ExtraMutator<T> {
impl<T: Config<I>, I: 'static> sp_std::ops::DerefMut for ExtraMutator<T, I> {
fn deref_mut(&mut self) -> &mut T::Extra {
if self.pending.is_none() {
self.pending = Some(self.original.clone());
Expand All @@ -57,34 +60,34 @@ impl<T: Config> sp_std::ops::DerefMut for ExtraMutator<T> {
}
}

impl<T: Config> ExtraMutator<T> {
pub(super) fn maybe_new(id: T::AssetId, who: impl sp_std::borrow::Borrow<T::AccountId>)
-> Option<ExtraMutator<T>>
{
if Account::<T>::contains_key(id, who.borrow()) {
Some(ExtraMutator::<T> {
impl<T: Config<I>, I: 'static> ExtraMutator<T, I> {
pub(super) fn maybe_new(
id: T::AssetId,
who: impl sp_std::borrow::Borrow<T::AccountId>,
) -> Option<ExtraMutator<T, I>> {
if Account::<T, I>::contains_key(id, who.borrow()) {
Some(ExtraMutator::<T, I> {
id,
who: who.borrow().clone(),
original: Account::<T>::get(id, who.borrow()).extra,
original: Account::<T, I>::get(id, who.borrow()).extra,
pending: None,
})
} else {
None
}
}


/// Commit any changes to storage.
pub fn commit(&mut self) -> Result<(), ()> {
if let Some(extra) = self.pending.take() {
Account::<T>::try_mutate_exists(self.id, self.who.borrow(), |maybe_account|
Account::<T, I>::try_mutate_exists(self.id, self.who.borrow(), |maybe_account| {
if let Some(ref mut account) = maybe_account {
account.extra = extra;
Ok(())
} else {
Err(())
}
)
})
} else {
Ok(())
}
Expand All @@ -93,13 +96,13 @@ impl<T: Config> ExtraMutator<T> {
/// Revert any changes, even those already committed by `self` and drop self.
pub fn revert(mut self) -> Result<(), ()> {
self.pending = None;
Account::<T>::try_mutate_exists(self.id, self.who.borrow(), |maybe_account|
Account::<T, I>::try_mutate_exists(self.id, self.who.borrow(), |maybe_account| {
if let Some(ref mut account) = maybe_account {
account.extra = self.original.clone();
Ok(())
} else {
Err(())
}
)
})
}
}
97 changes: 55 additions & 42 deletions frame/assets/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,40 @@
use super::*;

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

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

/// Get the asset `id` balance of `who`.
pub fn balance(id: T::AssetId, who: impl sp_std::borrow::Borrow<T::AccountId>) -> T::Balance {
Account::<T>::get(id, who.borrow()).balance
Account::<T, I>::get(id, who.borrow()).balance
}

/// Get the total supply of an asset `id`.
pub fn total_supply(id: T::AssetId) -> T::Balance {
Asset::<T>::get(id).map(|x| x.supply).unwrap_or_else(Zero::zero)
Asset::<T, I>::get(id)
.map(|x| x.supply)
.unwrap_or_else(Zero::zero)
}

pub(super) fn new_account(
who: &T::AccountId,
d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T>>,
d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
) -> Result<bool, DispatchError> {
let accounts = d.accounts.checked_add(1).ok_or(Error::<T>::Overflow)?;
let accounts = d.accounts.checked_add(1).ok_or(Error::<T, I>::Overflow)?;
let is_sufficient = if d.is_sufficient {
frame_system::Pallet::<T>::inc_sufficients(who);
d.sufficients += 1;
true
} else {
frame_system::Pallet::<T>::inc_consumers(who).map_err(|_| Error::<T>::NoProvider)?;
frame_system::Pallet::<T>::inc_consumers(who).map_err(|_| Error::<T, I>::NoProvider)?;
false
};
d.accounts = accounts;
Expand All @@ -60,7 +63,7 @@ impl<T: Config> Pallet<T> {
pub(super) fn dead_account(
what: T::AssetId,
who: &T::AccountId,
d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T>>,
d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
sufficient: bool,
) {
if sufficient {
Expand All @@ -73,15 +76,19 @@ impl<T: Config> Pallet<T> {
T::Freezer::died(what, who)
}

pub(super) fn can_increase(id: T::AssetId, who: &T::AccountId, amount: T::Balance) -> DepositConsequence {
let details = match Asset::<T>::get(id) {
pub(super) fn can_increase(
id: T::AssetId,
who: &T::AccountId,
amount: T::Balance,
) -> DepositConsequence {
let details = match Asset::<T, I>::get(id) {
Some(details) => details,
None => return DepositConsequence::UnknownAsset,
};
if details.supply.checked_add(&amount).is_none() {
return DepositConsequence::Overflow
}
let account = Account::<T>::get(id, who);
let account = Account::<T, I>::get(id, who);
if account.balance.checked_add(&amount).is_none() {
return DepositConsequence::Overflow
}
Expand All @@ -108,7 +115,7 @@ impl<T: Config> Pallet<T> {
keep_alive: bool,
) -> WithdrawConsequence<T::Balance> {
use WithdrawConsequence::*;
let details = match Asset::<T>::get(id) {
let details = match Asset::<T, I>::get(id) {
Some(details) => details,
None => return UnknownAsset,
};
Expand All @@ -118,7 +125,7 @@ impl<T: Config> Pallet<T> {
if details.is_frozen {
return Frozen
}
let account = Account::<T>::get(id, who);
let account = Account::<T, I>::get(id, who);
if account.is_frozen {
return Frozen
}
Expand Down Expand Up @@ -155,19 +162,21 @@ impl<T: Config> Pallet<T> {
id: T::AssetId,
who: &T::AccountId,
keep_alive: bool,
) -> Result<T::Balance, Error<T>> {
let details = match Asset::<T>::get(id) {
) -> Result<T::Balance, Error<T, I>> {
let details = match Asset::<T, I>::get(id) {
Some(details) => details,
None => return Err(Error::<T>::Unknown),
None => return Err(Error::<T, I>::Unknown),
};
ensure!(!details.is_frozen, Error::<T>::Frozen);
ensure!(!details.is_frozen, Error::<T, I>::Frozen);

let account = Account::<T>::get(id, who);
ensure!(!account.is_frozen, Error::<T>::Frozen);
let account = Account::<T, I>::get(id, who);
ensure!(!account.is_frozen, Error::<T, I>::Frozen);

let amount = if let Some(frozen) = T::Freezer::frozen_balance(id, who) {
// Frozen balance: account CANNOT be deleted
let required = frozen.checked_add(&details.min_balance).ok_or(Error::<T>::Overflow)?;
let required = frozen
.checked_add(&details.min_balance)
.ok_or(Error::<T, I>::Overflow)?;
account.balance.saturating_sub(required)
} else {
let is_provider = false;
Expand Down Expand Up @@ -204,9 +213,8 @@ impl<T: Config> Pallet<T> {
amount: T::Balance,
f: DebitFlags,
) -> Result<T::Balance, DispatchError> {
let actual = Self::reducible_balance(id, target, f.keep_alive)?
.min(amount);
ensure!(f.best_effort || actual >= amount, Error::<T>::BalanceLow);
let actual = Self::reducible_balance(id, target, f.keep_alive)?.min(amount);
ensure!(f.best_effort || actual >= amount, Error::<T, I>::BalanceLow);

let conseq = Self::can_decrease(id, target, actual, f.keep_alive);
let actual = match conseq.into_result() {
Expand Down Expand Up @@ -263,7 +271,10 @@ impl<T: Config> Pallet<T> {
) -> DispatchResult {
Self::increase_balance(id, beneficiary, amount, |details| -> DispatchResult {
if let Some(check_issuer) = maybe_check_issuer {
ensure!(&check_issuer == &details.issuer, Error::<T>::NoPermission);
ensure!(
&check_issuer == &details.issuer,
Error::<T, I>::NoPermission
);
}
debug_assert!(T::Balance::max_value() - details.supply >= amount, "checked in prep; qed");
details.supply = details.supply.saturating_add(amount);
Expand All @@ -283,17 +294,19 @@ impl<T: Config> Pallet<T> {
id: T::AssetId,
beneficiary: &T::AccountId,
amount: T::Balance,
check: impl FnOnce(&mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T>>) -> DispatchResult,
check: impl FnOnce(
&mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
) -> DispatchResult,
) -> DispatchResult {
if amount.is_zero() { return Ok(()) }

Self::can_increase(id, beneficiary, amount).into_result()?;
Asset::<T>::try_mutate(id, |maybe_details| -> DispatchResult {
let details = maybe_details.as_mut().ok_or(Error::<T>::Unknown)?;
Asset::<T, I>::try_mutate(id, |maybe_details| -> DispatchResult {
let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;

check(details)?;

Account::<T>::try_mutate(id, beneficiary, |t| -> DispatchResult {
Account::<T, I>::try_mutate(id, beneficiary, |t| -> DispatchResult {
let new_balance = t.balance.saturating_add(amount);
ensure!(new_balance >= details.min_balance, TokenError::BelowMinimum);
if t.balance.is_zero() {
Expand Down Expand Up @@ -324,7 +337,7 @@ impl<T: Config> Pallet<T> {
let actual = Self::decrease_balance(id, target, amount, f, |actual, details| {
// Check admin rights.
if let Some(check_admin) = maybe_check_admin {
ensure!(&check_admin == &details.admin, Error::<T>::NoPermission);
ensure!(&check_admin == &details.admin, Error::<T, I>::NoPermission);
}

debug_assert!(details.supply >= actual, "checked in prep; qed");
Expand All @@ -351,19 +364,19 @@ impl<T: Config> Pallet<T> {
f: DebitFlags,
check: impl FnOnce(
T::Balance,
&mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T>>,
&mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
) -> DispatchResult,
) -> Result<T::Balance, DispatchError> {
if amount.is_zero() { return Ok(amount) }

let actual = Self::prep_debit(id, target, amount, f)?;

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

check(actual, details)?;

Account::<T>::try_mutate_exists(id, target, |maybe_account| -> DispatchResult {
Account::<T, I>::try_mutate_exists(id, target, |maybe_account| -> DispatchResult {
let mut account = maybe_account.take().unwrap_or_default();
debug_assert!(account.balance >= actual, "checked in prep; qed");

Expand Down Expand Up @@ -411,14 +424,14 @@ impl<T: Config> Pallet<T> {
let debit = Self::prep_debit(id, &source, amount, f.into())?;
let (credit, maybe_burn) = Self::prep_credit(id, &dest, amount, debit, f.burn_dust)?;

let mut source_account = Account::<T>::get(id, &source);
let mut source_account = Account::<T, I>::get(id, &source);

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

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

// Skip if source == dest
Expand All @@ -437,7 +450,7 @@ impl<T: Config> Pallet<T> {
debug_assert!(source_account.balance >= debit, "checked in prep; qed");
source_account.balance = source_account.balance.saturating_sub(debit);

Account::<T>::try_mutate(id, &dest, |a| -> DispatchResult {
Account::<T, I>::try_mutate(id, &dest, |a| -> DispatchResult {
// Calculate new balance; this will not saturate since it's already checked in prep.
debug_assert!(a.balance.checked_add(&credit).is_some(), "checked in prep; qed");
let new_balance = a.balance.saturating_add(credit);
Expand All @@ -455,9 +468,9 @@ impl<T: Config> Pallet<T> {
if source_account.balance < details.min_balance {
debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
Self::dead_account(id, &source, details, source_account.sufficient);
Account::<T>::remove(id, &source);
Account::<T, I>::remove(id, &source);
} else {
Account::<T>::insert(id, &source, &source_account)
Account::<T, I>::insert(id, &source, &source_account)
}

Ok(())
Expand Down
Loading