Skip to content

Commit

Permalink
Implement do_transfer in pallet-balances
Browse files Browse the repository at this point in the history
* Implement do_transfer (related to issue paritytech#151 in Clamor).
* Block transfer on fungible implementation too

Co-authored-by: Giovanni Petrantoni <[email protected]>
  • Loading branch information
alebaffa and sinkingsugar authored Sep 14, 2022
1 parent 1b6228d commit 90e07df
Showing 1 changed file with 70 additions and 59 deletions.
129 changes: 70 additions & 59 deletions frame/balances/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,71 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
});
Ok(actual)
}

pub fn do_transfer(
transactor: &T::AccountId,
dest: &T::AccountId,
value: T::Balance,
existence_requirement: ExistenceRequirement,
) -> Result<T::Balance, DispatchError> {
if value.is_zero() || transactor == dest {
return Ok(value)
}

Self::try_mutate_account_with_dust(
dest,
|to_account, _| -> Result<DustCleaner<T, I>, DispatchError> {
Self::try_mutate_account_with_dust(
transactor,
|from_account, _| -> DispatchResult {
from_account.free = from_account
.free
.checked_sub(&value)
.ok_or(Error::<T, I>::InsufficientBalance)?;

// NOTE: total stake being stored in the same type means that this could
// never overflow but better to be safe than sorry.
to_account.free =
to_account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;

let ed = T::ExistentialDeposit::get();
ensure!(to_account.total() >= ed, Error::<T, I>::ExistentialDeposit);

Self::ensure_can_withdraw(
transactor,
value,
WithdrawReasons::TRANSFER,
from_account.free,
)
.map_err(|_| Error::<T, I>::LiquidityRestrictions)?;

// TODO: This is over-conservative. There may now be other providers, and
// this pallet may not even be a provider.
let allow_death = existence_requirement == ExistenceRequirement::AllowDeath;
let allow_death =
allow_death && system::Pallet::<T>::can_dec_provider(transactor);
ensure!(
allow_death || from_account.total() >= ed,
Error::<T, I>::KeepAlive
);

Ok(())
},
)
.map(|(_, maybe_dust_cleaner)| maybe_dust_cleaner)
},
)?;

// Emit transfer event.
Self::deposit_event(Event::Transfer {
from: transactor.clone(),
to: dest.clone(),
amount: value,
});

Ok(value)
}

}

impl<T: Config<I>, I: 'static> fungible::Inspect<T::AccountId> for Pallet<T, I> {
Expand Down Expand Up @@ -1139,8 +1204,9 @@ impl<T: Config<I>, I: 'static> fungible::Transfer<T::AccountId> for Pallet<T, I>
amount: T::Balance,
keep_alive: bool,
) -> Result<T::Balance, DispatchError> {
let er = if keep_alive { KeepAlive } else { AllowDeath };
<Self as Currency<T::AccountId>>::transfer(source, dest, amount, er).map(|_| amount)
ensure!(T::IsTransferable::get(), Error::<T, I>::CannotTransfer);
let existence_requirement = if keep_alive { KeepAlive } else { AllowDeath };
Self::do_transfer(source, dest, amount, existence_requirement).map(|_| amount)
}
}

Expand Down Expand Up @@ -1476,7 +1542,7 @@ where
}

// Transfer some free balance from `transactor` to `dest`, respecting existence requirements.
// Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest` or the
// Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest` or the
// transferability set in config is forbidden.
fn transfer(
transactor: &T::AccountId,
Expand All @@ -1485,62 +1551,7 @@ where
existence_requirement: ExistenceRequirement,
) -> DispatchResult {
ensure!(T::IsTransferable::get(), Error::<T, I>::CannotTransfer);

if value.is_zero() || transactor == dest {
return Ok(())
}

Self::try_mutate_account_with_dust(
dest,
|to_account, _| -> Result<DustCleaner<T, I>, DispatchError> {
Self::try_mutate_account_with_dust(
transactor,
|from_account, _| -> DispatchResult {
from_account.free = from_account
.free
.checked_sub(&value)
.ok_or(Error::<T, I>::InsufficientBalance)?;

// NOTE: total stake being stored in the same type means that this could
// never overflow but better to be safe than sorry.
to_account.free =
to_account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;

let ed = T::ExistentialDeposit::get();
ensure!(to_account.total() >= ed, Error::<T, I>::ExistentialDeposit);

Self::ensure_can_withdraw(
transactor,
value,
WithdrawReasons::TRANSFER,
from_account.free,
)
.map_err(|_| Error::<T, I>::LiquidityRestrictions)?;

// TODO: This is over-conservative. There may now be other providers, and
// this pallet may not even be a provider.
let allow_death = existence_requirement == ExistenceRequirement::AllowDeath;
let allow_death =
allow_death && system::Pallet::<T>::can_dec_provider(transactor);
ensure!(
allow_death || from_account.total() >= ed,
Error::<T, I>::KeepAlive
);

Ok(())
},
)
.map(|(_, maybe_dust_cleaner)| maybe_dust_cleaner)
},
)?;

// Emit transfer event.
Self::deposit_event(Event::Transfer {
from: transactor.clone(),
to: dest.clone(),
amount: value,
});

Self::do_transfer(transactor, dest, value, existence_requirement)?;
Ok(())
}

Expand Down

0 comments on commit 90e07df

Please sign in to comment.