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

Add low level traits to retrieve name, symbol, decimals and allowance in pallet-assets #9757

Merged
6 commits merged into from
Oct 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
125 changes: 125 additions & 0 deletions frame/assets/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
//! Functions for the Assets pallet.

use super::*;
use frame_support::{traits::Get, BoundedVec};

// The main implementation block for the module.
impl<T: Config<I>, I: 'static> Pallet<T, I> {
Expand Down Expand Up @@ -562,4 +563,128 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
})
})
}

/// Creates an approval from `owner` to spend `amount` of asset `id` tokens by 'delegate'
/// while reserving `T::ApprovalDeposit` from owner
///
/// If an approval already exists, the new amount is added to such existing approval
pub(super) fn do_approve_transfer(
id: T::AssetId,
owner: &T::AccountId,
delegate: &T::AccountId,
amount: T::Balance,
) -> DispatchResult {
let mut d = Asset::<T, I>::get(id).ok_or(Error::<T, I>::Unknown)?;
ensure!(!d.is_frozen, Error::<T, I>::Frozen);
Approvals::<T, I>::try_mutate(
(id, &owner, &delegate),
|maybe_approved| -> DispatchResult {
let mut approved = match maybe_approved.take() {
// an approval already exists and is being updated
Some(a) => a,
// a new approval is created
None => {
d.approvals.saturating_inc();
Default::default()
},
};
let deposit_required = T::ApprovalDeposit::get();
if approved.deposit < deposit_required {
T::Currency::reserve(&owner, deposit_required - approved.deposit)?;
approved.deposit = deposit_required;
}
approved.amount = approved.amount.saturating_add(amount);
*maybe_approved = Some(approved);
Ok(())
},
)?;
Asset::<T, I>::insert(id, d);
Self::deposit_event(Event::ApprovedTransfer(id, owner.clone(), delegate.clone(), amount));

Ok(())
}

/// Reduces the asset `id` balance of `owner` by some `amount` and increases the balance of
/// `dest` by (similar) amount, checking that 'delegate' has an existing approval from `owner`
/// to spend`amount`.
///
/// Will fail if `amount` is greater than the approval from `owner` to 'delegate'
/// Will unreserve the deposit from `owner` if the entire approved `amount` is spent by
/// 'delegate'
pub(super) fn do_transfer_approved(
id: T::AssetId,
owner: &T::AccountId,
delegate: &T::AccountId,
destination: &T::AccountId,
amount: T::Balance,
) -> DispatchResult {
Approvals::<T, I>::try_mutate_exists(
(id, &owner, delegate),
|maybe_approved| -> DispatchResult {
let mut approved = maybe_approved.take().ok_or(Error::<T, I>::Unapproved)?;
let remaining =
approved.amount.checked_sub(&amount).ok_or(Error::<T, I>::Unapproved)?;

let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
Self::do_transfer(id, &owner, &destination, amount, None, f)?;

if remaining.is_zero() {
T::Currency::unreserve(&owner, approved.deposit);
Asset::<T, I>::mutate(id, |maybe_details| {
if let Some(details) = maybe_details {
details.approvals.saturating_dec();
}
});
} else {
approved.amount = remaining;
*maybe_approved = Some(approved);
}
Ok(())
},
)?;
Ok(())
}

/// Do set metadata
pub(super) fn do_set_metadata(
id: T::AssetId,
from: &T::AccountId,
name: Vec<u8>,
symbol: Vec<u8>,
decimals: u8,
) -> DispatchResult {
let bounded_name: BoundedVec<u8, T::StringLimit> =
name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
let bounded_symbol: BoundedVec<u8, T::StringLimit> =
symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;

let d = Asset::<T, I>::get(id).ok_or(Error::<T, I>::Unknown)?;
ensure!(from == &d.owner, Error::<T, I>::NoPermission);

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

let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
let new_deposit = T::MetadataDepositPerByte::get()
.saturating_mul(((name.len() + symbol.len()) as u32).into())
.saturating_add(T::MetadataDepositBase::get());

if new_deposit > old_deposit {
T::Currency::reserve(from, new_deposit - old_deposit)?;
} else {
T::Currency::unreserve(from, old_deposit - new_deposit);
}

*metadata = Some(AssetMetadata {
deposit: new_deposit,
name: bounded_name,
symbol: bounded_symbol,
decimals,
is_frozen: false,
});

Self::deposit_event(Event::MetadataSet(id, name, symbol, decimals, false));
Ok(())
})
}
}
69 changes: 69 additions & 0 deletions frame/assets/src/impl_fungibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,72 @@ impl<T: Config<I>, I: 'static> fungibles::Destroy<T::AccountId> for Pallet<T, I>
Self::do_destroy(id, witness, maybe_check_owner)
}
}

impl<T: Config<I>, I: 'static> fungibles::metadata::Inspect<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
fn name(asset: T::AssetId) -> Vec<u8> {
Metadata::<T, I>::get(asset).name.to_vec()
}

fn symbol(asset: T::AssetId) -> Vec<u8> {
Metadata::<T, I>::get(asset).symbol.to_vec()
}

fn decimals(asset: T::AssetId) -> u8 {
Metadata::<T, I>::get(asset).decimals
}
}

impl<T: Config<I>, I: 'static> fungibles::metadata::Mutate<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
fn set(
asset: T::AssetId,
from: &<T as SystemConfig>::AccountId,
name: Vec<u8>,
symbol: Vec<u8>,
decimals: u8,
) -> DispatchResult {
Self::do_set_metadata(asset, from, name, symbol, decimals)
}
}

impl<T: Config<I>, I: 'static> fungibles::approvals::Inspect<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
// Check the amount approved to be spent by an owner to a delegate
fn allowance(
asset: T::AssetId,
owner: &<T as SystemConfig>::AccountId,
delegate: &<T as SystemConfig>::AccountId,
) -> T::Balance {
Approvals::<T, I>::get((asset, &owner, &delegate))
.map(|x| x.amount)
.unwrap_or_else(Zero::zero)
}
}

impl<T: Config<I>, I: 'static> fungibles::approvals::Mutate<<T as SystemConfig>::AccountId>
for Pallet<T, I>
{
fn approve(
asset: T::AssetId,
owner: &<T as SystemConfig>::AccountId,
delegate: &<T as SystemConfig>::AccountId,
amount: T::Balance,
) -> DispatchResult {
Self::do_approve_transfer(asset, owner, delegate, amount)
}

// Aprove spending tokens from a given account
fn transfer_from(
asset: T::AssetId,
owner: &<T as SystemConfig>::AccountId,
delegate: &<T as SystemConfig>::AccountId,
dest: &<T as SystemConfig>::AccountId,
amount: T::Balance,
) -> DispatchResult {
Self::do_transfer_approved(asset, owner, delegate, dest, amount)
}
}
96 changes: 3 additions & 93 deletions frame/assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,43 +948,7 @@ pub mod pallet {
decimals: u8,
) -> DispatchResult {
let origin = ensure_signed(origin)?;

let bounded_name: BoundedVec<u8, T::StringLimit> =
name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
let bounded_symbol: BoundedVec<u8, T::StringLimit> =
symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;

let d = Asset::<T, I>::get(id).ok_or(Error::<T, I>::Unknown)?;
ensure!(&origin == &d.owner, Error::<T, I>::NoPermission);

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

let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
let new_deposit = T::MetadataDepositPerByte::get()
.saturating_mul(((name.len() + symbol.len()) as u32).into())
.saturating_add(T::MetadataDepositBase::get());

if new_deposit > old_deposit {
T::Currency::reserve(&origin, new_deposit - old_deposit)?;
} else {
T::Currency::unreserve(&origin, old_deposit - new_deposit);
}

*metadata = Some(AssetMetadata {
deposit: new_deposit,
name: bounded_name,
symbol: bounded_symbol,
decimals,
is_frozen: false,
});

Self::deposit_event(Event::MetadataSet(id, name, symbol, decimals, false));
Ok(())
})
Self::do_set_metadata(id, &origin, name, symbol, decimals)
}

/// Clear the metadata for an asset.
Expand Down Expand Up @@ -1171,35 +1135,7 @@ pub mod pallet {
) -> DispatchResult {
let owner = ensure_signed(origin)?;
let delegate = T::Lookup::lookup(delegate)?;

let mut d = Asset::<T, I>::get(id).ok_or(Error::<T, I>::Unknown)?;
ensure!(!d.is_frozen, Error::<T, I>::Frozen);
Approvals::<T, I>::try_mutate(
(id, &owner, &delegate),
|maybe_approved| -> DispatchResult {
let mut approved = match maybe_approved.take() {
// an approval already exists and is being updated
Some(a) => a,
// a new approval is created
None => {
d.approvals.saturating_inc();
Default::default()
},
};
let deposit_required = T::ApprovalDeposit::get();
if approved.deposit < deposit_required {
T::Currency::reserve(&owner, deposit_required - approved.deposit)?;
approved.deposit = deposit_required;
}
approved.amount = approved.amount.saturating_add(amount);
*maybe_approved = Some(approved);
Ok(())
},
)?;
Asset::<T, I>::insert(id, d);
Self::deposit_event(Event::ApprovedTransfer(id, owner, delegate, amount));

Ok(())
Self::do_approve_transfer(id, &owner, &delegate, amount)
}

/// Cancel all of some asset approved for delegated transfer by a third-party account.
Expand Down Expand Up @@ -1306,33 +1242,7 @@ pub mod pallet {
let delegate = ensure_signed(origin)?;
let owner = T::Lookup::lookup(owner)?;
let destination = T::Lookup::lookup(destination)?;

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

let f =
TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
Self::do_transfer(id, &owner, &destination, amount, None, f)?;

if remaining.is_zero() {
T::Currency::unreserve(&owner, approved.deposit);
Asset::<T, I>::mutate(id, |maybe_details| {
if let Some(details) = maybe_details {
details.approvals.saturating_dec();
}
});
} else {
approved.amount = remaining;
*maybe_approved = Some(approved);
}
Ok(())
},
)?;
Ok(())
Self::do_transfer_approved(id, &owner, &delegate, &destination, amount)
}
}
}
34 changes: 34 additions & 0 deletions frame/assets/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,3 +794,37 @@ fn assets_from_genesis_should_exist() {
assert_eq!(Assets::total_supply(999), 100);
});
}

#[test]
fn querying_name_symbol_and_decimals_should_work() {
new_test_ext().execute_with(|| {
use frame_support::traits::tokens::fungibles::metadata::Inspect;
assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1));
assert_ok!(Assets::force_set_metadata(
Origin::root(),
0,
vec![0u8; 10],
vec![1u8; 10],
12,
false
));
assert_eq!(Assets::name(0), vec![0u8; 10]);
assert_eq!(Assets::symbol(0), vec![1u8; 10]);
assert_eq!(Assets::decimals(0), 12);
});
}

#[test]
fn querying_allowance_should_work() {
new_test_ext().execute_with(|| {
use frame_support::traits::tokens::fungibles::approvals::{Inspect, Mutate};
assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1));
assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100));
Balances::make_free_balance_be(&1, 1);
assert_ok!(Assets::approve(0, &1, &2, 50));
assert_eq!(Assets::allowance(0, &1, &2), 50);
// Transfer asset 0, from owner 1 and delegate 2 to destination 3
assert_ok!(Assets::transfer_from(0, &1, &2, &3, 50));
assert_eq!(Assets::allowance(0, &1, &2), 0);
});
}
2 changes: 2 additions & 0 deletions frame/support/src/traits/tokens/fungibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ use crate::dispatch::{DispatchError, DispatchResult};
use sp_runtime::traits::Saturating;
use sp_std::vec::Vec;

pub mod approvals;
mod balanced;
pub mod metadata;
pub use balanced::{Balanced, Unbalanced};
mod imbalance;
pub use imbalance::{CreditOf, DebtOf, HandleImbalanceDrop, Imbalance};
Expand Down
Loading