#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::unused_unit)]
pub use crate::imbalances::{NegativeImbalance, PositiveImbalance};
use frame_support::{
ensure,
pallet_prelude::*,
traits::{
BalanceStatus as Status, Currency as PalletCurrency, ExistenceRequirement, Get, Imbalance,
LockableCurrency as PalletLockableCurrency, ReservableCurrency as PalletReservableCurrency, SignedImbalance,
WithdrawReasons,
},
transactional,
};
use frame_system::{ensure_signed, pallet_prelude::*};
use orml_traits::{
account::MergeAccount,
arithmetic::{self, Signed},
BalanceStatus, GetByKey, LockIdentifier, MultiCurrency, MultiCurrencyExtended, MultiLockableCurrency,
MultiReservableCurrency, OnDust,
};
use sp_runtime::{
traits::{
AccountIdConversion, AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member,
Saturating, StaticLookup, Zero,
},
DispatchError, DispatchResult, ModuleId, RuntimeDebug,
};
use sp_std::{
convert::{Infallible, TryFrom, TryInto},
marker,
prelude::*,
vec::Vec,
};
mod default_weight;
mod imbalances;
mod mock;
mod tests;
pub struct TransferDust<T, GetAccountId>(marker::PhantomData<(T, GetAccountId)>);
impl<T, GetAccountId> OnDust<T::AccountId, T::CurrencyId, T::Balance> for TransferDust<T, GetAccountId>
where
T: Config,
GetAccountId: Get<T::AccountId>,
{
fn on_dust(who: &T::AccountId, currency_id: T::CurrencyId, amount: T::Balance) {
let _ = <Pallet<T> as MultiCurrency<T::AccountId>>::transfer(currency_id, who, &GetAccountId::get(), amount);
}
}
pub struct BurnDust<T>(marker::PhantomData<T>);
impl<T: Config> OnDust<T::AccountId, T::CurrencyId, T::Balance> for BurnDust<T> {
fn on_dust(who: &T::AccountId, currency_id: T::CurrencyId, amount: T::Balance) {
let _ = Pallet::<T>::withdraw(currency_id, who, amount);
}
}
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct BalanceLock<Balance> {
pub id: LockIdentifier,
pub amount: Balance,
}
#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug)]
pub struct AccountData<Balance> {
pub free: Balance,
pub reserved: Balance,
pub frozen: Balance,
}
impl<Balance: Saturating + Copy + Ord> AccountData<Balance> {
pub(crate) fn frozen(&self) -> Balance {
self.frozen
}
fn total(&self) -> Balance {
self.free.saturating_add(self.reserved)
}
}
pub use module::*;
#[frame_support::pallet]
pub mod module {
use super::*;
pub trait WeightInfo {
fn transfer() -> Weight;
fn transfer_all() -> Weight;
}
#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
type Balance: Parameter + Member + AtLeast32BitUnsigned + Default + Copy + MaybeSerializeDeserialize;
type Amount: Signed
+ TryInto<Self::Balance>
+ TryFrom<Self::Balance>
+ Parameter
+ Member
+ arithmetic::SimpleArithmetic
+ Default
+ Copy
+ MaybeSerializeDeserialize;
type CurrencyId: Parameter + Member + Copy + MaybeSerializeDeserialize + Ord;
type WeightInfo: WeightInfo;
type ExistentialDeposits: GetByKey<Self::CurrencyId, Self::Balance>;
type OnDust: OnDust<Self::AccountId, Self::CurrencyId, Self::Balance>;
}
#[pallet::error]
pub enum Error<T> {
BalanceTooLow,
BalanceOverflow,
TotalIssuanceOverflow,
AmountIntoBalanceFailed,
LiquidityRestrictions,
StillHasActiveReserved,
}
#[pallet::event]
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event<T: Config> {
Transferred(T::CurrencyId, T::AccountId, T::AccountId, T::Balance),
DustLost(T::AccountId, T::CurrencyId, T::Balance),
}
#[pallet::storage]
#[pallet::getter(fn total_issuance)]
pub type TotalIssuance<T: Config> = StorageMap<_, Twox64Concat, T::CurrencyId, T::Balance, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn locks)]
pub type Locks<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AccountId,
Twox64Concat,
T::CurrencyId,
Vec<BalanceLock<T::Balance>>,
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn accounts)]
pub type Accounts<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AccountId,
Twox64Concat,
T::CurrencyId,
AccountData<T::Balance>,
ValueQuery,
>;
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub endowed_accounts: Vec<(T::AccountId, T::CurrencyId, T::Balance)>,
}
#[cfg(feature = "std")]
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
GenesisConfig {
endowed_accounts: vec![],
}
}
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
let unique_endowed_accounts = self
.endowed_accounts
.iter()
.map(|(account_id, currency_id, _)| (account_id, currency_id))
.collect::<std::collections::BTreeSet<_>>();
assert!(
unique_endowed_accounts.len() == self.endowed_accounts.len(),
"duplicate endowed accounts in genesis."
);
self.endowed_accounts
.iter()
.for_each(|(account_id, currency_id, initial_balance)| {
assert!(
*initial_balance >= T::ExistentialDeposits::get(¤cy_id),
"the balance of any account should always be more than existential deposit.",
);
Pallet::<T>::mutate_account(account_id, *currency_id, |account_data, _| {
account_data.free = *initial_balance
});
TotalIssuance::<T>::mutate(*currency_id, |total_issuance| {
*total_issuance = total_issuance
.checked_add(initial_balance)
.expect("total issuance cannot overflow when building genesis")
});
});
}
}
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(T::WeightInfo::transfer())]
pub fn transfer(
origin: OriginFor<T>,
dest: <T::Lookup as StaticLookup>::Source,
currency_id: T::CurrencyId,
#[pallet::compact] amount: T::Balance,
) -> DispatchResultWithPostInfo {
let from = ensure_signed(origin)?;
let to = T::Lookup::lookup(dest)?;
<Self as MultiCurrency<_>>::transfer(currency_id, &from, &to, amount)?;
Self::deposit_event(Event::Transferred(currency_id, from, to, amount));
Ok(().into())
}
#[pallet::weight(T::WeightInfo::transfer_all())]
pub fn transfer_all(
origin: OriginFor<T>,
dest: <T::Lookup as StaticLookup>::Source,
currency_id: T::CurrencyId,
) -> DispatchResultWithPostInfo {
let from = ensure_signed(origin)?;
let to = T::Lookup::lookup(dest)?;
let balance = <Self as MultiCurrency<T::AccountId>>::free_balance(currency_id, &from);
<Self as MultiCurrency<T::AccountId>>::transfer(currency_id, &from, &to, balance)?;
Self::deposit_event(Event::Transferred(currency_id, from, to, balance));
Ok(().into())
}
}
}
impl<T: Config> Pallet<T> {
pub(crate) fn is_module_account_id(account_id: &T::AccountId) -> bool {
ModuleId::try_from_account(account_id).is_some()
}
pub(crate) fn try_mutate_account<R, E>(
who: &T::AccountId,
currency_id: T::CurrencyId,
f: impl FnOnce(&mut AccountData<T::Balance>, bool) -> sp_std::result::Result<R, E>,
) -> sp_std::result::Result<R, E> {
Accounts::<T>::try_mutate_exists(who, currency_id, |maybe_account| {
let existed = maybe_account.is_some();
let mut account = maybe_account.take().unwrap_or_default();
f(&mut account, existed).map(move |result| {
let mut handle_dust: Option<T::Balance> = None;
let total = account.total();
*maybe_account = if total.is_zero() {
None
} else {
if total < T::ExistentialDeposits::get(¤cy_id) && !Self::is_module_account_id(who) {
handle_dust = Some(total);
}
Some(account)
};
(existed, maybe_account.is_some(), handle_dust, result)
})
})
.map(|(existed, exists, handle_dust, result)| {
if existed && !exists {
let _ = frame_system::Module::<T>::dec_providers(who);
} else if !existed && exists {
frame_system::Module::<T>::inc_providers(who);
}
if let Some(dust_amount) = handle_dust {
T::OnDust::on_dust(who, currency_id, dust_amount);
Self::deposit_event(Event::DustLost(who.clone(), currency_id, dust_amount));
}
result
})
}
pub(crate) fn mutate_account<R>(
who: &T::AccountId,
currency_id: T::CurrencyId,
f: impl FnOnce(&mut AccountData<T::Balance>, bool) -> R,
) -> R {
Self::try_mutate_account(who, currency_id, |account, existed| -> Result<R, Infallible> {
Ok(f(account, existed))
})
.expect("Error is infallible; qed")
}
pub(crate) fn set_free_balance(currency_id: T::CurrencyId, who: &T::AccountId, amount: T::Balance) {
Self::mutate_account(who, currency_id, |account, _| {
account.free = amount;
});
}
pub(crate) fn set_reserved_balance(currency_id: T::CurrencyId, who: &T::AccountId, amount: T::Balance) {
Self::mutate_account(who, currency_id, |account, _| {
account.reserved = amount;
});
}
pub(crate) fn update_locks(currency_id: T::CurrencyId, who: &T::AccountId, locks: &[BalanceLock<T::Balance>]) {
Self::mutate_account(who, currency_id, |account, _| {
account.frozen = Zero::zero();
for lock in locks.iter() {
account.frozen = account.frozen.max(lock.amount);
}
});
let existed = <Locks<T>>::contains_key(who, currency_id);
if locks.is_empty() {
<Locks<T>>::remove(who, currency_id);
if existed {
frame_system::Module::<T>::dec_consumers(who);
}
} else {
<Locks<T>>::insert(who, currency_id, locks);
if !existed {
if frame_system::Module::<T>::inc_consumers(who).is_err() {
frame_support::debug::warn!(
"Warning: Attempt to introduce lock consumer reference, yet no providers. \
This is unexpected but should be safe."
);
}
}
}
}
}
impl<T: Config> MultiCurrency<T::AccountId> for Pallet<T> {
type CurrencyId = T::CurrencyId;
type Balance = T::Balance;
fn minimum_balance(currency_id: Self::CurrencyId) -> Self::Balance {
T::ExistentialDeposits::get(¤cy_id)
}
fn total_issuance(currency_id: Self::CurrencyId) -> Self::Balance {
<TotalIssuance<T>>::get(currency_id)
}
fn total_balance(currency_id: Self::CurrencyId, who: &T::AccountId) -> Self::Balance {
Self::accounts(who, currency_id).total()
}
fn free_balance(currency_id: Self::CurrencyId, who: &T::AccountId) -> Self::Balance {
Self::accounts(who, currency_id).free
}
fn ensure_can_withdraw(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
if amount.is_zero() {
return Ok(());
}
let new_balance = Self::free_balance(currency_id, who)
.checked_sub(&amount)
.ok_or(Error::<T>::BalanceTooLow)?;
ensure!(
new_balance >= Self::accounts(who, currency_id).frozen(),
Error::<T>::LiquidityRestrictions
);
Ok(())
}
fn transfer(
currency_id: Self::CurrencyId,
from: &T::AccountId,
to: &T::AccountId,
amount: Self::Balance,
) -> DispatchResult {
if amount.is_zero() || from == to {
return Ok(());
}
Self::ensure_can_withdraw(currency_id, from, amount)?;
let from_balance = Self::free_balance(currency_id, from);
let to_balance = Self::free_balance(currency_id, to)
.checked_add(&amount)
.ok_or(Error::<T>::BalanceOverflow)?;
Self::set_free_balance(currency_id, from, from_balance - amount);
Self::set_free_balance(currency_id, to, to_balance);
Ok(())
}
fn deposit(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
if amount.is_zero() {
return Ok(());
}
TotalIssuance::<T>::try_mutate(currency_id, |total_issuance| -> DispatchResult {
*total_issuance = total_issuance
.checked_add(&amount)
.ok_or(Error::<T>::TotalIssuanceOverflow)?;
Self::set_free_balance(currency_id, who, Self::free_balance(currency_id, who) + amount);
Ok(())
})
}
fn withdraw(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
if amount.is_zero() {
return Ok(());
}
Self::ensure_can_withdraw(currency_id, who, amount)?;
<TotalIssuance<T>>::mutate(currency_id, |v| *v -= amount);
Self::set_free_balance(currency_id, who, Self::free_balance(currency_id, who) - amount);
Ok(())
}
fn can_slash(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> bool {
if value.is_zero() {
return true;
}
Self::free_balance(currency_id, who) >= value
}
fn slash(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> Self::Balance {
if amount.is_zero() {
return amount;
}
let account = Self::accounts(who, currency_id);
let free_slashed_amount = account.free.min(amount);
let mut remaining_slash = amount - free_slashed_amount;
if !free_slashed_amount.is_zero() {
Self::set_free_balance(currency_id, who, account.free - free_slashed_amount);
}
if !remaining_slash.is_zero() {
let reserved_slashed_amount = account.reserved.min(remaining_slash);
remaining_slash -= reserved_slashed_amount;
Self::set_reserved_balance(currency_id, who, account.reserved - reserved_slashed_amount);
}
<TotalIssuance<T>>::mutate(currency_id, |v| *v -= amount - remaining_slash);
remaining_slash
}
}
impl<T: Config> MultiCurrencyExtended<T::AccountId> for Pallet<T> {
type Amount = T::Amount;
fn update_balance(currency_id: Self::CurrencyId, who: &T::AccountId, by_amount: Self::Amount) -> DispatchResult {
if by_amount.is_zero() {
return Ok(());
}
let by_amount_abs = if by_amount == Self::Amount::min_value() {
Self::Amount::max_value()
} else {
by_amount.abs()
};
let by_balance =
TryInto::<Self::Balance>::try_into(by_amount_abs).map_err(|_| Error::<T>::AmountIntoBalanceFailed)?;
if by_amount.is_positive() {
Self::deposit(currency_id, who, by_balance)
} else {
Self::withdraw(currency_id, who, by_balance).map(|_| ())
}
}
}
impl<T: Config> MultiLockableCurrency<T::AccountId> for Pallet<T> {
type Moment = T::BlockNumber;
fn set_lock(
lock_id: LockIdentifier,
currency_id: Self::CurrencyId,
who: &T::AccountId,
amount: Self::Balance,
) -> DispatchResult {
if amount.is_zero() {
return Ok(());
}
let mut new_lock = Some(BalanceLock { id: lock_id, amount });
let mut locks = Self::locks(who, currency_id)
.into_iter()
.filter_map(|lock| {
if lock.id == lock_id {
new_lock.take()
} else {
Some(lock)
}
})
.collect::<Vec<_>>();
if let Some(lock) = new_lock {
locks.push(lock)
}
Self::update_locks(currency_id, who, &locks[..]);
Ok(())
}
fn extend_lock(
lock_id: LockIdentifier,
currency_id: Self::CurrencyId,
who: &T::AccountId,
amount: Self::Balance,
) -> DispatchResult {
if amount.is_zero() {
return Ok(());
}
let mut new_lock = Some(BalanceLock { id: lock_id, amount });
let mut locks = Self::locks(who, currency_id)
.into_iter()
.filter_map(|lock| {
if lock.id == lock_id {
new_lock.take().map(|nl| BalanceLock {
id: lock.id,
amount: lock.amount.max(nl.amount),
})
} else {
Some(lock)
}
})
.collect::<Vec<_>>();
if let Some(lock) = new_lock {
locks.push(lock)
}
Self::update_locks(currency_id, who, &locks[..]);
Ok(())
}
fn remove_lock(lock_id: LockIdentifier, currency_id: Self::CurrencyId, who: &T::AccountId) -> DispatchResult {
let mut locks = Self::locks(who, currency_id);
locks.retain(|lock| lock.id != lock_id);
Self::update_locks(currency_id, who, &locks[..]);
Ok(())
}
}
impl<T: Config> MultiReservableCurrency<T::AccountId> for Pallet<T> {
fn can_reserve(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> bool {
if value.is_zero() {
return true;
}
Self::ensure_can_withdraw(currency_id, who, value).is_ok()
}
fn slash_reserved(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> Self::Balance {
if value.is_zero() {
return value;
}
let reserved_balance = Self::reserved_balance(currency_id, who);
let actual = reserved_balance.min(value);
Self::set_reserved_balance(currency_id, who, reserved_balance - actual);
<TotalIssuance<T>>::mutate(currency_id, |v| *v -= actual);
value - actual
}
fn reserved_balance(currency_id: Self::CurrencyId, who: &T::AccountId) -> Self::Balance {
Self::accounts(who, currency_id).reserved
}
fn reserve(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> DispatchResult {
if value.is_zero() {
return Ok(());
}
Self::ensure_can_withdraw(currency_id, who, value)?;
let account = Self::accounts(who, currency_id);
Self::set_free_balance(currency_id, who, account.free - value);
Self::set_reserved_balance(currency_id, who, account.reserved + value);
Ok(())
}
fn unreserve(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> Self::Balance {
if value.is_zero() {
return value;
}
let account = Self::accounts(who, currency_id);
let actual = account.reserved.min(value);
Self::set_reserved_balance(currency_id, who, account.reserved - actual);
Self::set_free_balance(currency_id, who, account.free + actual);
value - actual
}
fn repatriate_reserved(
currency_id: Self::CurrencyId,
slashed: &T::AccountId,
beneficiary: &T::AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> sp_std::result::Result<Self::Balance, DispatchError> {
if value.is_zero() {
return Ok(value);
}
if slashed == beneficiary {
return match status {
BalanceStatus::Free => Ok(Self::unreserve(currency_id, slashed, value)),
BalanceStatus::Reserved => Ok(value.saturating_sub(Self::reserved_balance(currency_id, slashed))),
};
}
let from_account = Self::accounts(slashed, currency_id);
let to_account = Self::accounts(beneficiary, currency_id);
let actual = from_account.reserved.min(value);
match status {
BalanceStatus::Free => {
Self::set_free_balance(currency_id, beneficiary, to_account.free + actual);
}
BalanceStatus::Reserved => {
Self::set_reserved_balance(currency_id, beneficiary, to_account.reserved + actual);
}
}
Self::set_reserved_balance(currency_id, slashed, from_account.reserved - actual);
Ok(value - actual)
}
}
pub struct CurrencyAdapter<T, GetCurrencyId>(marker::PhantomData<(T, GetCurrencyId)>);
impl<T, GetCurrencyId> PalletCurrency<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
where
T: Config,
GetCurrencyId: Get<T::CurrencyId>,
{
type Balance = T::Balance;
type PositiveImbalance = PositiveImbalance<T, GetCurrencyId>;
type NegativeImbalance = NegativeImbalance<T, GetCurrencyId>;
fn total_balance(who: &T::AccountId) -> Self::Balance {
Pallet::<T>::total_balance(GetCurrencyId::get(), who)
}
fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
Pallet::<T>::can_slash(GetCurrencyId::get(), who, value)
}
fn total_issuance() -> Self::Balance {
Pallet::<T>::total_issuance(GetCurrencyId::get())
}
fn minimum_balance() -> Self::Balance {
Pallet::<T>::minimum_balance(GetCurrencyId::get())
}
fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
if amount.is_zero() {
return PositiveImbalance::zero();
}
<TotalIssuance<T>>::mutate(GetCurrencyId::get(), |issued| {
*issued = issued.checked_sub(&amount).unwrap_or_else(|| {
amount = *issued;
Zero::zero()
});
});
PositiveImbalance::new(amount)
}
fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
if amount.is_zero() {
return NegativeImbalance::zero();
}
<TotalIssuance<T>>::mutate(GetCurrencyId::get(), |issued| {
*issued = issued.checked_add(&amount).unwrap_or_else(|| {
amount = Self::Balance::max_value() - *issued;
Self::Balance::max_value()
})
});
NegativeImbalance::new(amount)
}
fn free_balance(who: &T::AccountId) -> Self::Balance {
Pallet::<T>::free_balance(GetCurrencyId::get(), who)
}
fn ensure_can_withdraw(
who: &T::AccountId,
amount: Self::Balance,
_reasons: WithdrawReasons,
_new_balance: Self::Balance,
) -> DispatchResult {
Pallet::<T>::ensure_can_withdraw(GetCurrencyId::get(), who, amount)
}
fn transfer(
source: &T::AccountId,
dest: &T::AccountId,
value: Self::Balance,
_existence_requirement: ExistenceRequirement,
) -> DispatchResult {
<Pallet<T> as MultiCurrency<T::AccountId>>::transfer(GetCurrencyId::get(), &source, &dest, value)
}
fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
if value.is_zero() {
return (Self::NegativeImbalance::zero(), value);
}
let currency_id = GetCurrencyId::get();
let account = Pallet::<T>::accounts(who, currency_id);
let free_slashed_amount = account.free.min(value);
let mut remaining_slash = value - free_slashed_amount;
if !free_slashed_amount.is_zero() {
Pallet::<T>::set_free_balance(currency_id, who, account.free - free_slashed_amount);
}
if !remaining_slash.is_zero() {
let reserved_slashed_amount = account.reserved.min(remaining_slash);
remaining_slash -= reserved_slashed_amount;
Pallet::<T>::set_reserved_balance(currency_id, who, account.reserved - reserved_slashed_amount);
(
Self::NegativeImbalance::new(free_slashed_amount + reserved_slashed_amount),
remaining_slash,
)
} else {
(Self::NegativeImbalance::new(value), remaining_slash)
}
}
fn deposit_into_existing(
who: &T::AccountId,
value: Self::Balance,
) -> sp_std::result::Result<Self::PositiveImbalance, DispatchError> {
if value.is_zero() {
return Ok(Self::PositiveImbalance::zero());
}
let currency_id = GetCurrencyId::get();
let new_total = Pallet::<T>::free_balance(currency_id, who)
.checked_add(&value)
.ok_or(Error::<T>::TotalIssuanceOverflow)?;
Pallet::<T>::set_free_balance(currency_id, who, new_total);
Ok(Self::PositiveImbalance::new(value))
}
fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance {
Self::deposit_into_existing(who, value).unwrap_or_else(|_| Self::PositiveImbalance::zero())
}
fn withdraw(
who: &T::AccountId,
value: Self::Balance,
_reasons: WithdrawReasons,
_liveness: ExistenceRequirement,
) -> sp_std::result::Result<Self::NegativeImbalance, DispatchError> {
if value.is_zero() {
return Ok(Self::NegativeImbalance::zero());
}
let currency_id = GetCurrencyId::get();
Pallet::<T>::ensure_can_withdraw(currency_id, who, value)?;
Pallet::<T>::set_free_balance(currency_id, who, Pallet::<T>::free_balance(currency_id, who) - value);
Ok(Self::NegativeImbalance::new(value))
}
fn make_free_balance_be(
who: &T::AccountId,
value: Self::Balance,
) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
let currency_id = GetCurrencyId::get();
Pallet::<T>::try_mutate_account(
who,
currency_id,
|account, existed| -> Result<SignedImbalance<Self::Balance, Self::PositiveImbalance>, ()> {
let ed = T::ExistentialDeposits::get(¤cy_id);
ensure!(value.saturating_add(account.reserved) >= ed || existed, ());
let imbalance = if account.free <= value {
SignedImbalance::Positive(PositiveImbalance::new(value - account.free))
} else {
SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
};
account.free = value;
Ok(imbalance)
},
)
.unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero()))
}
}
impl<T, GetCurrencyId> PalletReservableCurrency<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
where
T: Config,
GetCurrencyId: Get<T::CurrencyId>,
{
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
Pallet::<T>::can_reserve(GetCurrencyId::get(), who, value)
}
fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
let actual = Pallet::<T>::slash_reserved(GetCurrencyId::get(), who, value);
(Self::NegativeImbalance::zero(), actual)
}
fn reserved_balance(who: &T::AccountId) -> Self::Balance {
Pallet::<T>::reserved_balance(GetCurrencyId::get(), who)
}
fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
Pallet::<T>::reserve(GetCurrencyId::get(), who, value)
}
fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
Pallet::<T>::unreserve(GetCurrencyId::get(), who, value)
}
fn repatriate_reserved(
slashed: &T::AccountId,
beneficiary: &T::AccountId,
value: Self::Balance,
status: Status,
) -> sp_std::result::Result<Self::Balance, DispatchError> {
Pallet::<T>::repatriate_reserved(GetCurrencyId::get(), slashed, beneficiary, value, status)
}
}
impl<T, GetCurrencyId> PalletLockableCurrency<T::AccountId> for CurrencyAdapter<T, GetCurrencyId>
where
T: Config,
GetCurrencyId: Get<T::CurrencyId>,
{
type Moment = T::BlockNumber;
type MaxLocks = ();
fn set_lock(id: LockIdentifier, who: &T::AccountId, amount: Self::Balance, _reasons: WithdrawReasons) {
let _ = Pallet::<T>::set_lock(id, GetCurrencyId::get(), who, amount);
}
fn extend_lock(id: LockIdentifier, who: &T::AccountId, amount: Self::Balance, _reasons: WithdrawReasons) {
let _ = Pallet::<T>::extend_lock(id, GetCurrencyId::get(), who, amount);
}
fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
let _ = Pallet::<T>::remove_lock(id, GetCurrencyId::get(), who);
}
}
impl<T: Config> MergeAccount<T::AccountId> for Pallet<T> {
#[transactional]
fn merge_account(source: &T::AccountId, dest: &T::AccountId) -> DispatchResult {
Accounts::<T>::iter_prefix(source).try_for_each(|(currency_id, account_data)| -> DispatchResult {
ensure!(account_data.reserved.is_zero(), Error::<T>::StillHasActiveReserved);
<Self as MultiCurrency<T::AccountId>>::transfer(currency_id, source, dest, account_data.free)?;
Ok(())
})
}
}