Skip to content

Commit ce51811

Browse files
authored
[FRAME] Short-circuit fungible self transfer (paritytech#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>
1 parent 8d41d77 commit ce51811

File tree

7 files changed

+81
-15
lines changed

7 files changed

+81
-15
lines changed

substrate/frame/balances/src/tests/currency_tests.rs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@
1818
//! Tests regarding the functionality of the `Currency` trait set implementations.
1919
2020
use super::*;
21-
use crate::NegativeImbalance;
22-
use frame_support::traits::{
23-
BalanceStatus::{Free, Reserved},
24-
Currency,
25-
ExistenceRequirement::{self, AllowDeath, KeepAlive},
26-
Hooks, LockIdentifier, LockableCurrency, NamedReservableCurrency, ReservableCurrency,
27-
WithdrawReasons,
21+
use crate::{Event, NegativeImbalance};
22+
use frame_support::{
23+
traits::{
24+
BalanceStatus::{Free, Reserved},
25+
Currency,
26+
ExistenceRequirement::{self, AllowDeath, KeepAlive},
27+
Hooks, LockIdentifier, LockableCurrency, NamedReservableCurrency, ReservableCurrency,
28+
WithdrawReasons,
29+
},
30+
StorageNoopGuard,
2831
};
32+
use frame_system::Event as SysEvent;
2933

3034
const ID_1: LockIdentifier = *b"1 ";
3135
const ID_2: LockIdentifier = *b"2 ";
@@ -1363,3 +1367,39 @@ fn freezing_and_locking_should_work() {
13631367
assert_eq!(System::consumers(&1), 0);
13641368
});
13651369
}
1370+
1371+
#[test]
1372+
fn self_transfer_noop() {
1373+
ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| {
1374+
assert_eq!(Balances::total_issuance(), 0);
1375+
let _ = Balances::deposit_creating(&1, 100);
1376+
1377+
// The account is set up properly:
1378+
assert_eq!(
1379+
events(),
1380+
[
1381+
Event::Deposit { who: 1, amount: 100 }.into(),
1382+
SysEvent::NewAccount { account: 1 }.into(),
1383+
Event::Endowed { account: 1, free_balance: 100 }.into(),
1384+
]
1385+
);
1386+
assert_eq!(Balances::free_balance(1), 100);
1387+
assert_eq!(Balances::total_issuance(), 100);
1388+
1389+
// Transfers to self are No-OPs:
1390+
let _g = StorageNoopGuard::new();
1391+
for i in 0..200 {
1392+
let r = Balances::transfer_allow_death(Some(1).into(), 1, i);
1393+
1394+
if i <= 100 {
1395+
assert_ok!(r);
1396+
} else {
1397+
assert!(r.is_err());
1398+
}
1399+
1400+
assert!(events().is_empty());
1401+
assert_eq!(Balances::free_balance(1), 100, "Balance unchanged by self transfer");
1402+
assert_eq!(Balances::total_issuance(), 100, "TI unchanged by self transfers");
1403+
}
1404+
});
1405+
}

substrate/frame/broker/src/test_fungibles.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ where
168168

169169
impl<
170170
Instance: Get<u32>,
171-
AccountId: Encode,
171+
AccountId: Encode + Eq,
172172
AssetId: tokens::AssetId + Copy,
173173
MinimumBalance: TypedGet,
174174
HoldReason,

substrate/frame/nis/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl<T> fungible::Unbalanced<T> for NoCounterpart<T> {
148148
}
149149
fn set_total_issuance(_: Self::Balance) {}
150150
}
151-
impl<T> FunMutate<T> for NoCounterpart<T> {}
151+
impl<T: Eq> FunMutate<T> for NoCounterpart<T> {}
152152
impl<T> Convert<Perquintill, u32> for NoCounterpart<T> {
153153
fn convert(_: Perquintill) -> u32 {
154154
0

substrate/frame/support/src/traits/tokens/fungible/item_of.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ impl<
219219
impl<
220220
F: fungibles::Mutate<AccountId>,
221221
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
222-
AccountId,
222+
AccountId: Eq,
223223
> Mutate<AccountId> for ItemOf<F, A, AccountId>
224224
{
225225
fn mint_into(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {

substrate/frame/support/src/traits/tokens/fungible/regular.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,10 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
235235
}
236236

237237
/// Trait for providing a basic fungible asset.
238-
pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
238+
pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId>
239+
where
240+
AccountId: Eq,
241+
{
239242
/// Increase the balance of `who` by exactly `amount`, minting new tokens. If that isn't
240243
/// possible then an `Err` is returned and nothing is changed.
241244
fn mint_into(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
@@ -303,6 +306,9 @@ pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
303306
}
304307

305308
/// Transfer funds from one account into another.
309+
///
310+
/// A transfer where the source and destination account are identical is treated as No-OP after
311+
/// checking the preconditions.
306312
fn transfer(
307313
source: &AccountId,
308314
dest: &AccountId,
@@ -311,6 +317,10 @@ pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
311317
) -> Result<Self::Balance, DispatchError> {
312318
let _extra = Self::can_withdraw(source, amount).into_result(preservation != Expendable)?;
313319
Self::can_deposit(dest, amount, Extant).into_result()?;
320+
if source == dest {
321+
return Ok(amount)
322+
}
323+
314324
Self::decrease_balance(source, amount, BestEffort, preservation, Polite)?;
315325
// This should never fail as we checked `can_deposit` earlier. But we do a best-effort
316326
// anyway.

substrate/frame/support/src/traits/tokens/fungibles/regular.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,10 @@ pub trait Unbalanced<AccountId>: Inspect<AccountId> {
250250
}
251251

252252
/// Trait for providing a basic fungible asset.
253-
pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
253+
pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId>
254+
where
255+
AccountId: Eq,
256+
{
254257
/// Increase the balance of `who` by exactly `amount`, minting new tokens. If that isn't
255258
/// possible then an `Err` is returned and nothing is changed.
256259
fn mint_into(
@@ -353,6 +356,9 @@ pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
353356
}
354357

355358
/// Transfer funds from one account into another.
359+
///
360+
/// A transfer where the source and destination account are identical is treated as No-OP after
361+
/// checking the preconditions.
356362
fn transfer(
357363
asset: Self::AssetId,
358364
source: &AccountId,
@@ -363,6 +369,10 @@ pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
363369
let _extra = Self::can_withdraw(asset.clone(), source, amount)
364370
.into_result(preservation != Expendable)?;
365371
Self::can_deposit(asset.clone(), dest, amount, Extant).into_result()?;
372+
if source == dest {
373+
return Ok(amount)
374+
}
375+
366376
Self::decrease_balance(asset.clone(), source, amount, BestEffort, preservation, Polite)?;
367377
// This should never fail as we checked `can_deposit` earlier. But we do a best-effort
368378
// anyway.

substrate/frame/support/src/traits/tokens/pay.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,13 @@ pub enum PaymentStatus {
8282
}
8383

8484
/// Simple implementation of `Pay` which makes a payment from a "pot" - i.e. a single account.
85-
pub struct PayFromAccount<F, A>(sp_std::marker::PhantomData<(F, A)>);
86-
impl<A: TypedGet, F: fungible::Mutate<A::Type>> Pay for PayFromAccount<F, A> {
85+
pub struct PayFromAccount<F, A>(core::marker::PhantomData<(F, A)>);
86+
impl<A, F> Pay for PayFromAccount<F, A>
87+
where
88+
A: TypedGet,
89+
F: fungible::Mutate<A::Type>,
90+
A::Type: Eq,
91+
{
8792
type Balance = F::Balance;
8893
type Beneficiary = A::Type;
8994
type AssetKind = ();
@@ -110,11 +115,12 @@ impl<A: TypedGet, F: fungible::Mutate<A::Type>> Pay for PayFromAccount<F, A> {
110115

111116
/// Simple implementation of `Pay` for assets which makes a payment from a "pot" - i.e. a single
112117
/// account.
113-
pub struct PayAssetFromAccount<F, A>(sp_std::marker::PhantomData<(F, A)>);
118+
pub struct PayAssetFromAccount<F, A>(core::marker::PhantomData<(F, A)>);
114119
impl<A, F> frame_support::traits::tokens::Pay for PayAssetFromAccount<F, A>
115120
where
116121
A: TypedGet,
117122
F: fungibles::Mutate<A::Type> + fungibles::Create<A::Type>,
123+
A::Type: Eq,
118124
{
119125
type Balance = F::Balance;
120126
type Beneficiary = A::Type;

0 commit comments

Comments
 (0)