pallet-asset: Fix transfer of a large amount of an asset (#11241)

* pallet-asset: Fix transfer of a large amount of an asset

Before this pr transferring a large amount of an asset would check that transferring the asset would
not overflow the supply of the asset. However, it doesn't make sense to check for asset supply
overflow when we just transfer from one account to another account and don't increase the supply in
any way. It also required to extend the `can_deposit` method of `fungible` and `fungibles` with a
`mint` parameter. If this parameter is set to `true`, it means we want to mint the amount of an
asset before transferring it into an account. For `can_withdraw` we don't need to add an extra
parameter, because withdrawing should never be able to underflow the supply. If that would happen,
it would mean that somewhere the supply wasn't increased while increasing the balance of an account.

* Update frame/assets/src/functions.rs

* Update frame/assets/src/functions.rs

* Update frame/assets/src/functions.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* FMT

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Bastian Köcher
2022-04-25 20:12:06 +02:00
committed by GitHub
parent 914db49d04
commit 2541efdbcc
6 changed files with 47 additions and 13 deletions
@@ -50,7 +50,11 @@ pub trait Inspect<AccountId> {
fn reducible_balance(who: &AccountId, keep_alive: bool) -> Self::Balance;
/// Returns `true` if the balance of `who` may be increased by `amount`.
fn can_deposit(who: &AccountId, amount: Self::Balance) -> DepositConsequence;
///
/// - `who`: The account of which the balance should be increased by `amount`.
/// - `amount`: How much should the balance be increased?
/// - `mint`: Will `amount` be minted to deposit it into `account`?
fn can_deposit(who: &AccountId, amount: Self::Balance, mint: bool) -> DepositConsequence;
/// Returns `Failed` if the balance of `who` may not be decreased by `amount`, otherwise
/// the consequence.
@@ -86,7 +90,9 @@ pub trait Mutate<AccountId>: Inspect<AccountId> {
amount: Self::Balance,
) -> Result<Self::Balance, DispatchError> {
let extra = Self::can_withdraw(&source, amount).into_result()?;
Self::can_deposit(&dest, amount.saturating_add(extra)).into_result()?;
// As we first burn and then mint, we don't need to check if `mint` fits into the supply.
// If we can withdraw/burn it, we can also mint it again.
Self::can_deposit(&dest, amount.saturating_add(extra), false).into_result()?;
let actual = Self::burn_from(source, amount)?;
debug_assert!(
actual == amount.saturating_add(extra),
@@ -216,8 +222,8 @@ impl<
fn reducible_balance(who: &AccountId, keep_alive: bool) -> Self::Balance {
<F as fungibles::Inspect<AccountId>>::reducible_balance(A::get(), who, keep_alive)
}
fn can_deposit(who: &AccountId, amount: Self::Balance) -> DepositConsequence {
<F as fungibles::Inspect<AccountId>>::can_deposit(A::get(), who, amount)
fn can_deposit(who: &AccountId, amount: Self::Balance, mint: bool) -> DepositConsequence {
<F as fungibles::Inspect<AccountId>>::can_deposit(A::get(), who, amount, mint)
}
fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance> {
<F as fungibles::Inspect<AccountId>>::can_withdraw(A::get(), who, amount)
@@ -53,10 +53,16 @@ pub trait Inspect<AccountId> {
fn reducible_balance(asset: Self::AssetId, who: &AccountId, keep_alive: bool) -> Self::Balance;
/// Returns `true` if the `asset` balance of `who` may be increased by `amount`.
///
/// - `asset`: The asset that should be deposited.
/// - `who`: The account of which the balance should be increased by `amount`.
/// - `amount`: How much should the balance be increased?
/// - `mint`: Will `amount` be minted to deposit it into `account`?
fn can_deposit(
asset: Self::AssetId,
who: &AccountId,
amount: Self::Balance,
mint: bool,
) -> DepositConsequence;
/// Returns `Failed` if the `asset` balance of `who` may not be decreased by `amount`, otherwise
@@ -137,7 +143,9 @@ pub trait Mutate<AccountId>: Inspect<AccountId> {
amount: Self::Balance,
) -> Result<Self::Balance, DispatchError> {
let extra = Self::can_withdraw(asset, &source, amount).into_result()?;
Self::can_deposit(asset, &dest, amount.saturating_add(extra)).into_result()?;
// As we first burn and then mint, we don't need to check if `mint` fits into the supply.
// If we can withdraw/burn it, we can also mint it again.
Self::can_deposit(asset, &dest, amount.saturating_add(extra), false).into_result()?;
let actual = Self::burn_from(asset, source, amount)?;
debug_assert!(
actual == amount.saturating_add(extra),