Newer
Older
//! # Currencies Module
//!
//! ## Overview
//!
//! The currencies module provides a mixed currencies system, by configuring a
//! native currency which implements `BasicCurrencyExtended`, and a
//! multi-currency which implements `MultiCurrency`.
//! It also provides an adapter, to adapt `frame_support::traits::Currency`
//! implementations into `BasicCurrencyExtended`.
//! The currencies module provides functionality of both `MultiCurrencyExtended`
//! and `BasicCurrencyExtended`, via unified interfaces, and all calls would be
//! delegated to the underlying multi-currency and base currency system.
//! A native currency ID could be set by `Config::GetNativeCurrencyId`, to
//!
//! ### Implementations
//!
//! The currencies module provides implementations for following traits.
//!
//! - `MultiCurrency` - Abstraction over a fungible multi-currency system.
//! - `MultiCurrencyExtended` - Extended `MultiCurrency` with additional helper
//! types and methods, like updating balance
//! by a given signed integer amount.
//!
//! ## Interface
//!
//! ### Dispatchable Functions
//!
//! - `transfer` - Transfer some balance to another account, in a given
//! currency.
//! - `transfer_native_currency` - Transfer some balance to another account, in
//! native currency set in
//! - `update_balance` - Update balance by signed integer amount, in a given
//! currency, root origin required.
use codec::Codec;
use frame_support::{
pallet_prelude::*,
traits::{
Currency as PalletCurrency, ExistenceRequirement, Get, LockableCurrency as PalletLockableCurrency,
ReservableCurrency as PalletReservableCurrency, WithdrawReasons,
},
};
use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
use orml_traits::{
arithmetic::{Signed, SimpleArithmetic},
BalanceStatus, BasicCurrency, BasicCurrencyExtended, BasicLockableCurrency, BasicReservableCurrency,
LockIdentifier, MultiCurrency, MultiCurrencyExtended, MultiLockableCurrency, MultiReservableCurrency,
};
use orml_utilities::with_transaction_result;
use sp_runtime::{
traits::{CheckedSub, MaybeSerializeDeserialize, StaticLookup, Zero},
DispatchError, DispatchResult,
};
use sp_std::{
convert::{TryFrom, TryInto},
fmt::Debug,
marker, result,
};
pub use weights::WeightInfo;
#[frame_support::pallet]
pub mod module {
pub(crate) type BalanceOf<T> =
<<T as Config>::MultiCurrency as MultiCurrency<<T as frame_system::Config>::AccountId>>::Balance;
pub(crate) type CurrencyIdOf<T> =
<<T as Config>::MultiCurrency as MultiCurrency<<T as frame_system::Config>::AccountId>>::CurrencyId;
pub(crate) type AmountOf<T> =
<<T as Config>::MultiCurrency as MultiCurrencyExtended<<T as frame_system::Config>::AccountId>>::Amount;
#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
type MultiCurrency: TransferAll<Self::AccountId>
+ MultiCurrencyExtended<Self::AccountId>
+ MultiLockableCurrency<Self::AccountId>
+ MultiReservableCurrency<Self::AccountId>;
type NativeCurrency: BasicCurrencyExtended<Self::AccountId, Balance = BalanceOf<Self>, Amount = AmountOf<Self>>
+ BasicLockableCurrency<Self::AccountId, Balance = BalanceOf<Self>>
+ BasicReservableCurrency<Self::AccountId, Balance = BalanceOf<Self>>;
#[pallet::constant]
type GetNativeCurrencyId: Get<CurrencyIdOf<Self>>;
/// Weight information for extrinsics in this module.
type WeightInfo: WeightInfo;
}
#[pallet::error]
pub enum Error<T> {
BalanceTooLow,
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
#[pallet::metadata(CurrencyIdOf<T> = "Currency", T::AccountId = "AccountId", BalanceOf<T> = "Balance", AmountOf<T> = "Amount")]
/// Currency transfer success. \[currency_id, from, to, amount\]
Transferred(CurrencyIdOf<T>, T::AccountId, T::AccountId, BalanceOf<T>),
/// Update balance success. \[currency_id, who, amount\]
BalanceUpdated(CurrencyIdOf<T>, T::AccountId, AmountOf<T>),
/// Deposit success. \[currency_id, who, amount\]
Deposited(CurrencyIdOf<T>, T::AccountId, BalanceOf<T>),
/// Withdraw success. \[currency_id, who, amount\]
Withdrawn(CurrencyIdOf<T>, T::AccountId, BalanceOf<T>),
}
#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Transfer some balance to another account under `currency_id`.
///
/// The dispatch origin for this call must be `Signed` by the
/// transactor.
#[pallet::weight(T::WeightInfo::transfer_non_native_currency())]
currency_id: CurrencyIdOf<T>,
#[pallet::compact] amount: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
let from = ensure_signed(origin)?;
let to = T::Lookup::lookup(dest)?;
<Self as MultiCurrency<T::AccountId>>::transfer(currency_id, &from, &to, amount)?;
/// Transfer some native currency to another account.
///
/// The dispatch origin for this call must be `Signed` by the
/// transactor.
#[pallet::weight(T::WeightInfo::transfer_native_currency())]
#[pallet::compact] amount: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
let from = ensure_signed(origin)?;
let to = T::Lookup::lookup(dest)?;
T::NativeCurrency::transfer(&from, &to, amount)?;
Self::deposit_event(Event::Transferred(T::GetNativeCurrencyId::get(), from, to, amount));
Ok(().into())
/// update amount of account `who` under `currency_id`.
///
/// The dispatch origin of this call must be _Root_.
#[pallet::weight(T::WeightInfo::update_balance_non_native_currency())]
who: <T::Lookup as StaticLookup>::Source,
currency_id: CurrencyIdOf<T>,
amount: AmountOf<T>,
ensure_root(origin)?;
let dest = T::Lookup::lookup(who)?;
<Self as MultiCurrencyExtended<T::AccountId>>::update_balance(currency_id, &dest, amount)?;
impl<T: Config> MultiCurrency<T::AccountId> for Pallet<T> {
type CurrencyId = CurrencyIdOf<T>;
type Balance = BalanceOf<T>;
fn minimum_balance(currency_id: Self::CurrencyId) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::minimum_balance()
} else {
T::MultiCurrency::minimum_balance(currency_id)
fn total_issuance(currency_id: Self::CurrencyId) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::total_issuance()
} else {
T::MultiCurrency::total_issuance(currency_id)
fn total_balance(currency_id: Self::CurrencyId, who: &T::AccountId) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::total_balance(who)
} else {
T::MultiCurrency::total_balance(currency_id, who)
fn free_balance(currency_id: Self::CurrencyId, who: &T::AccountId) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::free_balance(who)
} else {
T::MultiCurrency::free_balance(currency_id, who)
fn ensure_can_withdraw(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::ensure_can_withdraw(who, amount)
} else {
T::MultiCurrency::ensure_can_withdraw(currency_id, who, amount)
fn transfer(
currency_id: Self::CurrencyId,
from: &T::AccountId,
to: &T::AccountId,
amount: Self::Balance,
) -> DispatchResult {
if amount.is_zero() || from == to {
return Ok(());
}
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::transfer(from, to, amount)?;
} else {
T::MultiCurrency::transfer(currency_id, from, to, amount)?;
}
Self::deposit_event(Event::Transferred(currency_id, from.clone(), to.clone(), amount));
Ok(())
}
fn deposit(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
if amount.is_zero() {
return Ok(());
}
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::deposit(who, amount)?;
} else {
T::MultiCurrency::deposit(currency_id, who, amount)?;
Self::deposit_event(Event::Deposited(currency_id, who.clone(), amount));
Ok(())
}
fn withdraw(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
if amount.is_zero() {
return Ok(());
}
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::withdraw(who, amount)?;
} else {
T::MultiCurrency::withdraw(currency_id, who, amount)?;
Self::deposit_event(Event::Withdrawn(currency_id, who.clone(), amount));
Ok(())
}
fn can_slash(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> bool {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::can_slash(who, amount)
} else {
T::MultiCurrency::can_slash(currency_id, who, amount)
fn slash(currency_id: Self::CurrencyId, who: &T::AccountId, amount: Self::Balance) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::slash(who, amount)
} else {
T::MultiCurrency::slash(currency_id, who, amount)
impl<T: Config> MultiCurrencyExtended<T::AccountId> for Pallet<T> {
type Amount = AmountOf<T>;
fn update_balance(currency_id: Self::CurrencyId, who: &T::AccountId, by_amount: Self::Amount) -> DispatchResult {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::update_balance(who, by_amount)?;
} else {
T::MultiCurrency::update_balance(currency_id, who, by_amount)?;
Self::deposit_event(Event::BalanceUpdated(currency_id, who.clone(), by_amount));
Ok(())
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 currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::set_lock(lock_id, who, amount)
} else {
T::MultiCurrency::set_lock(lock_id, currency_id, who, amount)
fn extend_lock(
lock_id: LockIdentifier,
currency_id: Self::CurrencyId,
who: &T::AccountId,
amount: Self::Balance,
) -> DispatchResult {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::extend_lock(lock_id, who, amount)
} else {
T::MultiCurrency::extend_lock(lock_id, currency_id, who, amount)
fn remove_lock(lock_id: LockIdentifier, currency_id: Self::CurrencyId, who: &T::AccountId) -> DispatchResult {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::remove_lock(lock_id, who)
} else {
T::MultiCurrency::remove_lock(lock_id, currency_id, who)
impl<T: Config> MultiReservableCurrency<T::AccountId> for Pallet<T> {
fn can_reserve(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> bool {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::can_reserve(who, value)
} else {
T::MultiCurrency::can_reserve(currency_id, who, value)
fn slash_reserved(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::slash_reserved(who, value)
} else {
T::MultiCurrency::slash_reserved(currency_id, who, value)
fn reserved_balance(currency_id: Self::CurrencyId, who: &T::AccountId) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::reserved_balance(who)
} else {
T::MultiCurrency::reserved_balance(currency_id, who)
fn reserve(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> DispatchResult {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::reserve(who, value)
} else {
T::MultiCurrency::reserve(currency_id, who, value)
fn unreserve(currency_id: Self::CurrencyId, who: &T::AccountId, value: Self::Balance) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::unreserve(who, value)
} else {
T::MultiCurrency::unreserve(currency_id, who, value)
fn repatriate_reserved(
currency_id: Self::CurrencyId,
slashed: &T::AccountId,
beneficiary: &T::AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> result::Result<Self::Balance, DispatchError> {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::repatriate_reserved(slashed, beneficiary, value, status)
} else {
T::MultiCurrency::repatriate_reserved(currency_id, slashed, beneficiary, value, status)
pub struct Currency<T, GetCurrencyId>(marker::PhantomData<T>, marker::PhantomData<GetCurrencyId>);
impl<T, GetCurrencyId> BasicCurrency<T::AccountId> for Currency<T, GetCurrencyId>
where
T: Config,
GetCurrencyId: Get<CurrencyIdOf<T>>,
{
type Balance = BalanceOf<T>;
fn minimum_balance() -> Self::Balance {
<Pallet<T>>::minimum_balance(GetCurrencyId::get())
}
fn total_issuance() -> Self::Balance {
<Pallet<T>>::total_issuance(GetCurrencyId::get())
}
fn total_balance(who: &T::AccountId) -> Self::Balance {
<Pallet<T>>::total_balance(GetCurrencyId::get(), who)
}
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) -> DispatchResult {
<Pallet<T>>::ensure_can_withdraw(GetCurrencyId::get(), who, amount)
}
fn transfer(from: &T::AccountId, to: &T::AccountId, amount: Self::Balance) -> DispatchResult {
<Pallet<T> as MultiCurrency<T::AccountId>>::transfer(GetCurrencyId::get(), from, to, amount)
}
fn deposit(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
<Pallet<T>>::deposit(GetCurrencyId::get(), who, amount)
}
fn withdraw(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
<Pallet<T>>::withdraw(GetCurrencyId::get(), who, amount)
}
fn can_slash(who: &T::AccountId, amount: Self::Balance) -> bool {
<Pallet<T>>::can_slash(GetCurrencyId::get(), who, amount)
}
fn slash(who: &T::AccountId, amount: Self::Balance) -> Self::Balance {
<Pallet<T>>::slash(GetCurrencyId::get(), who, amount)
impl<T, GetCurrencyId> BasicCurrencyExtended<T::AccountId> for Currency<T, GetCurrencyId>
where
T: Config,
GetCurrencyId: Get<CurrencyIdOf<T>>,
{
type Amount = AmountOf<T>;
fn update_balance(who: &T::AccountId, by_amount: Self::Amount) -> DispatchResult {
<Pallet<T> as MultiCurrencyExtended<T::AccountId>>::update_balance(GetCurrencyId::get(), who, by_amount)
impl<T, GetCurrencyId> BasicLockableCurrency<T::AccountId> for Currency<T, GetCurrencyId>
where
T: Config,
GetCurrencyId: Get<CurrencyIdOf<T>>,
{
type Moment = T::BlockNumber;
fn set_lock(lock_id: LockIdentifier, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
<Pallet<T> as MultiLockableCurrency<T::AccountId>>::set_lock(lock_id, GetCurrencyId::get(), who, amount)
}
fn extend_lock(lock_id: LockIdentifier, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
<Pallet<T> as MultiLockableCurrency<T::AccountId>>::extend_lock(lock_id, GetCurrencyId::get(), who, amount)
}
fn remove_lock(lock_id: LockIdentifier, who: &T::AccountId) -> DispatchResult {
<Pallet<T> as MultiLockableCurrency<T::AccountId>>::remove_lock(lock_id, GetCurrencyId::get(), who)
impl<T, GetCurrencyId> BasicReservableCurrency<T::AccountId> for Currency<T, GetCurrencyId>
where
T: Config,
GetCurrencyId: Get<CurrencyIdOf<T>>,
{
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
<Pallet<T> as MultiReservableCurrency<T::AccountId>>::can_reserve(GetCurrencyId::get(), who, value)
}
fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
<Pallet<T> as MultiReservableCurrency<T::AccountId>>::slash_reserved(GetCurrencyId::get(), who, value)
}
fn reserved_balance(who: &T::AccountId) -> Self::Balance {
<Pallet<T> as MultiReservableCurrency<T::AccountId>>::reserved_balance(GetCurrencyId::get(), who)
}
fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
<Pallet<T> as MultiReservableCurrency<T::AccountId>>::reserve(GetCurrencyId::get(), who, value)
}
fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
<Pallet<T> as MultiReservableCurrency<T::AccountId>>::unreserve(GetCurrencyId::get(), who, value)
}
fn repatriate_reserved(
slashed: &T::AccountId,
beneficiary: &T::AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> result::Result<Self::Balance, DispatchError> {
<Pallet<T> as MultiReservableCurrency<T::AccountId>>::repatriate_reserved(
GetCurrencyId::get(),
slashed,
beneficiary,
value,
status,
)
pub type NativeCurrencyOf<T> = Currency<T, <T as Config>::GetNativeCurrencyId>;
/// Adapt other currency traits implementation to `BasicCurrency`.
pub struct BasicCurrencyAdapter<T, Currency, Amount, Moment>(marker::PhantomData<(T, Currency, Amount, Moment)>);
type PalletBalanceOf<A, Currency> = <Currency as PalletCurrency<A>>::Balance;
// Adapt `frame_support::traits::Currency`
impl<T, AccountId, Currency, Amount, Moment> BasicCurrency<AccountId>
for BasicCurrencyAdapter<T, Currency, Amount, Moment>
where
Currency: PalletCurrency<AccountId>,
T: Config,
{
type Balance = PalletBalanceOf<AccountId, Currency>;
fn minimum_balance() -> Self::Balance {
Currency::minimum_balance()
}
fn total_issuance() -> Self::Balance {
Currency::total_issuance()
}
fn total_balance(who: &AccountId) -> Self::Balance {
Currency::total_balance(who)
}
fn free_balance(who: &AccountId) -> Self::Balance {
Currency::free_balance(who)
}
fn ensure_can_withdraw(who: &AccountId, amount: Self::Balance) -> DispatchResult {
let new_balance = Self::free_balance(who)
.checked_sub(&amount)
.ok_or(Error::<T>::BalanceTooLow)?;
Currency::ensure_can_withdraw(who, amount, WithdrawReasons::all(), new_balance)
}
fn transfer(from: &AccountId, to: &AccountId, amount: Self::Balance) -> DispatchResult {
Currency::transfer(from, to, amount, ExistenceRequirement::AllowDeath)
}
fn deposit(who: &AccountId, amount: Self::Balance) -> DispatchResult {
let _ = Currency::deposit_creating(who, amount);
Ok(())
}
fn withdraw(who: &AccountId, amount: Self::Balance) -> DispatchResult {
Currency::withdraw(who, amount, WithdrawReasons::all(), ExistenceRequirement::AllowDeath).map(|_| ())
}
fn can_slash(who: &AccountId, amount: Self::Balance) -> bool {
Currency::can_slash(who, amount)
}
fn slash(who: &AccountId, amount: Self::Balance) -> Self::Balance {
let (_, gap) = Currency::slash(who, amount);
gap
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
// Adapt `frame_support::traits::Currency`
impl<T, AccountId, Currency, Amount, Moment> BasicCurrencyExtended<AccountId>
for BasicCurrencyAdapter<T, Currency, Amount, Moment>
where
Amount: Signed
+ TryInto<PalletBalanceOf<AccountId, Currency>>
+ TryFrom<PalletBalanceOf<AccountId, Currency>>
+ SimpleArithmetic
+ Codec
+ Copy
+ MaybeSerializeDeserialize
+ Debug
+ Default,
Currency: PalletCurrency<AccountId>,
T: Config,
{
type Amount = Amount;
fn update_balance(who: &AccountId, by_amount: Self::Amount) -> DispatchResult {
let by_balance = by_amount
.abs()
.try_into()
.map_err(|_| Error::<T>::AmountIntoBalanceFailed)?;
if by_amount.is_positive() {
Self::deposit(who, by_balance)
} else {
Self::withdraw(who, by_balance)
// Adapt `frame_support::traits::LockableCurrency`
impl<T, AccountId, Currency, Amount, Moment> BasicLockableCurrency<AccountId>
for BasicCurrencyAdapter<T, Currency, Amount, Moment>
where
Currency: PalletLockableCurrency<AccountId>,
T: Config,
{
type Moment = Moment;
fn set_lock(lock_id: LockIdentifier, who: &AccountId, amount: Self::Balance) -> DispatchResult {
Currency::set_lock(lock_id, who, amount, WithdrawReasons::all());
Ok(())
}
fn extend_lock(lock_id: LockIdentifier, who: &AccountId, amount: Self::Balance) -> DispatchResult {
Currency::extend_lock(lock_id, who, amount, WithdrawReasons::all());
Ok(())
}
fn remove_lock(lock_id: LockIdentifier, who: &AccountId) -> DispatchResult {
Currency::remove_lock(lock_id, who);
Ok(())
// Adapt `frame_support::traits::ReservableCurrency`
impl<T, AccountId, Currency, Amount, Moment> BasicReservableCurrency<AccountId>
for BasicCurrencyAdapter<T, Currency, Amount, Moment>
where
Currency: PalletReservableCurrency<AccountId>,
T: Config,
{
fn can_reserve(who: &AccountId, value: Self::Balance) -> bool {
Currency::can_reserve(who, value)
}
fn slash_reserved(who: &AccountId, value: Self::Balance) -> Self::Balance {
let (_, gap) = Currency::slash_reserved(who, value);
gap
}
fn reserved_balance(who: &AccountId) -> Self::Balance {
Currency::reserved_balance(who)
}
fn reserve(who: &AccountId, value: Self::Balance) -> DispatchResult {
Currency::reserve(who, value)
}
fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance {
Currency::unreserve(who, value)
}
fn repatriate_reserved(
slashed: &AccountId,
beneficiary: &AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> result::Result<Self::Balance, DispatchError> {
Currency::repatriate_reserved(slashed, beneficiary, value, status)
impl<T: Config> TransferAll<T::AccountId> for Pallet<T> {
fn transfer_all(source: &T::AccountId, dest: &T::AccountId) -> DispatchResult {
with_transaction_result(|| {
// transfer non-native free to dest
T::MultiCurrency::transfer_all(source, dest)?;
// transfer all free to dest
T::NativeCurrency::transfer(source, dest, T::NativeCurrency::free_balance(source))
})