[FRAME] Short-circuit fungible self transfer (#2118)

Changes:
- Change the fungible(s) logic to treat a self-transfer as No-OP (as
long as all pre-checks pass).

Note that the self-transfer case will not emit an event since no state
was changed.

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Oliver Tale-Yazdi
2023-11-01 22:11:28 +01:00
committed by GitHub
parent 4f05f9a686
commit c66ae375e6
10 changed files with 89 additions and 23 deletions
@@ -219,7 +219,7 @@ impl<
impl<
F: fungibles::Mutate<AccountId>,
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
AccountId,
AccountId: Eq,
> Mutate<AccountId> for ItemOf<F, A, AccountId>
{
fn mint_into(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
@@ -235,7 +235,10 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
}
/// Trait for providing a basic fungible asset.
pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId>
where
AccountId: Eq,
{
/// Increase the balance of `who` by exactly `amount`, minting new tokens. If that isn't
/// possible then an `Err` is returned and nothing is changed.
fn mint_into(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
@@ -303,6 +306,9 @@ pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
}
/// Transfer funds from one account into another.
///
/// A transfer where the source and destination account are identical is treated as No-OP after
/// checking the preconditions.
fn transfer(
source: &AccountId,
dest: &AccountId,
@@ -311,6 +317,10 @@ pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
) -> Result<Self::Balance, DispatchError> {
let _extra = Self::can_withdraw(source, amount).into_result(preservation != Expendable)?;
Self::can_deposit(dest, amount, Extant).into_result()?;
if source == dest {
return Ok(amount)
}
Self::decrease_balance(source, amount, BestEffort, preservation, Polite)?;
// This should never fail as we checked `can_deposit` earlier. But we do a best-effort
// anyway.
@@ -250,7 +250,10 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
}
/// Trait for providing a basic fungible asset.
pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId>
where
AccountId: Eq,
{
/// Increase the balance of `who` by exactly `amount`, minting new tokens. If that isn't
/// possible then an `Err` is returned and nothing is changed.
fn mint_into(
@@ -353,6 +356,9 @@ pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
}
/// Transfer funds from one account into another.
///
/// A transfer where the source and destination account are identical is treated as No-OP after
/// checking the preconditions.
fn transfer(
asset: Self::AssetId,
source: &AccountId,
@@ -363,6 +369,10 @@ pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
let _extra = Self::can_withdraw(asset.clone(), source, amount)
.into_result(preservation != Expendable)?;
Self::can_deposit(asset.clone(), dest, amount, Extant).into_result()?;
if source == dest {
return Ok(amount)
}
Self::decrease_balance(asset.clone(), source, amount, BestEffort, preservation, Polite)?;
// This should never fail as we checked `can_deposit` earlier. But we do a best-effort
// anyway.
@@ -82,8 +82,13 @@ pub enum PaymentStatus {
}
/// Simple implementation of `Pay` which makes a payment from a "pot" - i.e. a single account.
pub struct PayFromAccount<F, A>(sp_std::marker::PhantomData<(F, A)>);
impl<A: TypedGet, F: fungible::Mutate<A::Type>> Pay for PayFromAccount<F, A> {
pub struct PayFromAccount<F, A>(core::marker::PhantomData<(F, A)>);
impl<A, F> Pay for PayFromAccount<F, A>
where
A: TypedGet,
F: fungible::Mutate<A::Type>,
A::Type: Eq,
{
type Balance = F::Balance;
type Beneficiary = A::Type;
type AssetKind = ();
@@ -110,11 +115,12 @@ impl<A: TypedGet, F: fungible::Mutate<A::Type>> Pay for PayFromAccount<F, A> {
/// Simple implementation of `Pay` for assets which makes a payment from a "pot" - i.e. a single
/// account.
pub struct PayAssetFromAccount<F, A>(sp_std::marker::PhantomData<(F, A)>);
pub struct PayAssetFromAccount<F, A>(core::marker::PhantomData<(F, A)>);
impl<A, F> frame_support::traits::tokens::Pay for PayAssetFromAccount<F, A>
where
A: TypedGet,
F: fungibles::Mutate<A::Type> + fungibles::Create<A::Type>,
A::Type: Eq,
{
type Balance = F::Balance;
type Beneficiary = A::Type;