Skip to content

Implement NamedMultiReservableCurrency and NamedBasicReservableCurrency in orml_currencies #743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
162 changes: 159 additions & 3 deletions currencies/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ use frame_support::{
pallet_prelude::*,
traits::{
Currency as PalletCurrency, ExistenceRequirement, Get, Imbalance, LockableCurrency as PalletLockableCurrency,
ReservableCurrency as PalletReservableCurrency, WithdrawReasons,
NamedReservableCurrency as PalletNamedReservableCurrency, ReservableCurrency as PalletReservableCurrency,
WithdrawReasons,
},
};
use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
Expand All @@ -53,6 +54,7 @@ use orml_traits::{
currency::TransferAll,
BalanceStatus, BasicCurrency, BasicCurrencyExtended, BasicLockableCurrency, BasicReservableCurrency,
LockIdentifier, MultiCurrency, MultiCurrencyExtended, MultiLockableCurrency, MultiReservableCurrency,
NamedBasicReservableCurrency, NamedMultiReservableCurrency,
};
use orml_utilities::with_transaction_result;
use sp_runtime::{
Expand All @@ -78,17 +80,22 @@ pub mod module {
<<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;
pub(crate) type ReserveIdentifierOf<T> = <<T as Config>::MultiCurrency as NamedMultiReservableCurrency<
<T as frame_system::Config>::AccountId,
>>::ReserveIdentifier;

#[pallet::config]
pub trait Config: frame_system::Config {
type MultiCurrency: TransferAll<Self::AccountId>
+ MultiCurrencyExtended<Self::AccountId>
+ MultiLockableCurrency<Self::AccountId>
+ MultiReservableCurrency<Self::AccountId>;
+ MultiReservableCurrency<Self::AccountId>
+ NamedMultiReservableCurrency<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>>;
+ BasicReservableCurrency<Self::AccountId, Balance = BalanceOf<Self>>
+ NamedBasicReservableCurrency<Self::AccountId, ReserveIdentifierOf<Self>, Balance = BalanceOf<Self>>;

#[pallet::constant]
type GetNativeCurrencyId: Get<CurrencyIdOf<Self>>;
Expand Down Expand Up @@ -368,6 +375,76 @@ impl<T: Config> MultiReservableCurrency<T::AccountId> for Pallet<T> {
}
}

impl<T: Config> NamedMultiReservableCurrency<T::AccountId> for Pallet<T> {
type ReserveIdentifier = ReserveIdentifierOf<T>;

fn slash_reserved_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
who: &T::AccountId,
value: Self::Balance,
) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::slash_reserved_named(id, who, value)
} else {
T::MultiCurrency::slash_reserved_named(id, currency_id, who, value)
}
}

fn reserved_balance_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
who: &T::AccountId,
) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::reserved_balance_named(id, who)
} else {
T::MultiCurrency::reserved_balance_named(id, currency_id, who)
}
}

fn reserve_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
who: &T::AccountId,
value: Self::Balance,
) -> DispatchResult {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::reserve_named(id, who, value)
} else {
T::MultiCurrency::reserve_named(id, currency_id, who, value)
}
}

fn unreserve_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
who: &T::AccountId,
value: Self::Balance,
) -> Self::Balance {
if currency_id == T::GetNativeCurrencyId::get() {
T::NativeCurrency::unreserve_named(id, who, value)
} else {
T::MultiCurrency::unreserve_named(id, currency_id, who, value)
}
}

fn repatriate_reserved_named(
id: &Self::ReserveIdentifier,
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_named(id, slashed, beneficiary, value, status)
} else {
T::MultiCurrency::repatriate_reserved_named(id, 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>
Expand Down Expand Up @@ -491,6 +568,50 @@ where
}
}

impl<T, GetCurrencyId> NamedBasicReservableCurrency<T::AccountId, ReserveIdentifierOf<T>> for Currency<T, GetCurrencyId>
where
T: Config,
GetCurrencyId: Get<CurrencyIdOf<T>>,
{
fn slash_reserved_named(id: &ReserveIdentifierOf<T>, who: &T::AccountId, value: Self::Balance) -> Self::Balance {
<Pallet<T> as NamedMultiReservableCurrency<T::AccountId>>::slash_reserved_named(
id,
GetCurrencyId::get(),
who,
value,
)
}

fn reserved_balance_named(id: &ReserveIdentifierOf<T>, who: &T::AccountId) -> Self::Balance {
<Pallet<T> as NamedMultiReservableCurrency<T::AccountId>>::reserved_balance_named(id, GetCurrencyId::get(), who)
}

fn reserve_named(id: &ReserveIdentifierOf<T>, who: &T::AccountId, value: Self::Balance) -> DispatchResult {
<Pallet<T> as NamedMultiReservableCurrency<T::AccountId>>::reserve_named(id, GetCurrencyId::get(), who, value)
}

fn unreserve_named(id: &ReserveIdentifierOf<T>, who: &T::AccountId, value: Self::Balance) -> Self::Balance {
<Pallet<T> as NamedMultiReservableCurrency<T::AccountId>>::unreserve_named(id, GetCurrencyId::get(), who, value)
}

fn repatriate_reserved_named(
id: &ReserveIdentifierOf<T>,
slashed: &T::AccountId,
beneficiary: &T::AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> result::Result<Self::Balance, DispatchError> {
<Pallet<T> as NamedMultiReservableCurrency<T::AccountId>>::repatriate_reserved_named(
id,
GetCurrencyId::get(),
slashed,
beneficiary,
value,
status,
)
}
}

pub type NativeCurrencyOf<T> = Currency<T, <T as Config>::GetNativeCurrencyId>;

/// Adapt other currency traits implementation to `BasicCurrency`.
Expand Down Expand Up @@ -653,6 +774,41 @@ where
}
}

// Adapt `frame_support::traits::NamedReservableCurrency`
impl<T, AccountId, Currency, Amount, Moment, ReserveIdentifier>
NamedBasicReservableCurrency<AccountId, ReserveIdentifier> for BasicCurrencyAdapter<T, Currency, Amount, Moment>
where
Currency: PalletNamedReservableCurrency<AccountId, ReserveIdentifier = ReserveIdentifier>,
T: Config,
{
fn slash_reserved_named(id: &ReserveIdentifier, who: &AccountId, value: Self::Balance) -> Self::Balance {
let (_, gap) = Currency::slash_reserved_named(id, who, value);
gap
}

fn reserved_balance_named(id: &ReserveIdentifier, who: &AccountId) -> Self::Balance {
Currency::reserved_balance_named(id, who)
}

fn reserve_named(id: &ReserveIdentifier, who: &AccountId, value: Self::Balance) -> DispatchResult {
Currency::reserve_named(id, who, value)
}

fn unreserve_named(id: &ReserveIdentifier, who: &AccountId, value: Self::Balance) -> Self::Balance {
Currency::unreserve_named(id, who, value)
}

fn repatriate_reserved_named(
id: &ReserveIdentifier,
slashed: &AccountId,
beneficiary: &AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> result::Result<Self::Balance, DispatchError> {
Currency::repatriate_reserved_named(id, 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(|| {
Expand Down
126 changes: 124 additions & 2 deletions traits/src/currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ pub trait NamedMultiReservableCurrency<AccountId>: MultiReservableCurrency<Accou
/// cannot fail.
///
/// As much funds up to `value` will be deducted as possible. If the reserve
/// balance of `who` is less than `value`, then a non-zero excess will
/// be returned.
/// balance of `who` is less than `value`, then a non-zero excess will be
/// returned.
fn slash_reserved_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
Expand Down Expand Up @@ -512,6 +512,128 @@ pub trait BasicReservableCurrency<AccountId>: BasicCurrency<AccountId> {
) -> result::Result<Self::Balance, DispatchError>;
}

/// A fungible single currency system where funds can be reserved from the user
/// with an identifier.
pub trait NamedBasicReservableCurrency<AccountId, ReserveIdentifier>: BasicReservableCurrency<AccountId> {
/// Deducts up to `value` from reserved balance of `who`. This function
/// cannot fail.
///
/// As much funds up to `value` will be deducted as possible. If the reserve
/// balance of `who` is less than `value`, then a non-zero excess will be
/// returned.
fn slash_reserved_named(id: &ReserveIdentifier, who: &AccountId, value: Self::Balance) -> Self::Balance;

/// The amount of the balance of a given account that is externally
/// reserved; this can still get slashed, but gets slashed last of all.
///
/// This balance is a 'reserve' balance that other subsystems use in order
/// to set aside tokens that are still 'owned' by the account holder, but
/// which are suspendable.
///
/// When this balance falls below the value of `ExistentialDeposit`, then
/// this 'reserve account' is deleted: specifically, `ReservedBalance`.
///
/// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it
/// also gets collapsed to zero if it ever becomes less than
/// `ExistentialDeposit`.
fn reserved_balance_named(id: &ReserveIdentifier, who: &AccountId) -> Self::Balance;

/// Moves `value` from balance to reserved balance.
///
/// If the free balance is lower than `value`, then no funds will be moved
/// and an `Err` will be returned to notify of this. This is different
/// behavior than `unreserve`.
fn reserve_named(id: &ReserveIdentifier, who: &AccountId, value: Self::Balance) -> DispatchResult;

/// Moves up to `value` from reserved balance to free balance. This function
/// cannot fail.
///
/// As much funds up to `value` will be moved as possible. If the reserve
/// balance of `who` is less than `value`, then the remaining amount will be
/// returned.
///
/// # NOTES
///
/// - This is different from `reserve`.
/// - If the remaining reserved balance is less than `ExistentialDeposit`,
/// it will
/// invoke `on_reserved_too_low` and could reap the account.
fn unreserve_named(id: &ReserveIdentifier, who: &AccountId, value: Self::Balance) -> Self::Balance;

/// Moves up to `value` from reserved balance of account `slashed` to
/// balance of account `beneficiary`. `beneficiary` must exist for this to
/// succeed. If it does not, `Err` will be returned. Funds will be placed in
/// either the `free` balance or the `reserved` balance, depending on the
/// `status`.
///
/// As much funds up to `value` will be deducted as possible. If this is
/// less than `value`, then `Ok(non_zero)` will be returned.
fn repatriate_reserved_named(
id: &ReserveIdentifier,
slashed: &AccountId,
beneficiary: &AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> Result<Self::Balance, DispatchError>;

/// Ensure the reserved balance is equal to `value`.
///
/// This will reserve extra amount of current reserved balance is less than
/// `value`. And unreserve if current reserved balance is greater than
/// `value`.
fn ensure_reserved_named(id: &ReserveIdentifier, who: &AccountId, value: Self::Balance) -> DispatchResult {
let current = Self::reserved_balance_named(id, who);
match current.cmp(&value) {
Ordering::Less => {
// we checked value > current
Self::reserve_named(id, who, value - current)
}
Ordering::Equal => Ok(()),
Ordering::Greater => {
// we always have enough balance to unreserve here
Self::unreserve_named(id, who, current - value);
Ok(())
}
}
}

/// Unreserve all the named reserved balances, returning unreserved amount.
///
/// Is a no-op if the value to be unreserved is zero.
fn unreserve_all_named(id: &ReserveIdentifier, who: &AccountId) -> Self::Balance {
let value = Self::reserved_balance_named(id, who);
Self::unreserve_named(id, who, value);
value
}

/// Slash all the reserved balance, returning the negative imbalance
/// created.
///
/// Is a no-op if the value to be slashed is zero.
fn slash_all_reserved_named(id: &ReserveIdentifier, who: &AccountId) -> Self::Balance {
let value = Self::reserved_balance_named(id, who);
Self::slash_reserved_named(id, who, value)
}

/// Move all the named reserved balance of one account into the balance of
/// another, according to `status`. If `status` is `Reserved`, the balance
/// will be reserved with given `id`.
///
/// Is a no-op if:
/// - the value to be moved is zero; or
/// - the `slashed` id equal to `beneficiary` and the `status` is
/// `Reserved`.
fn repatriate_all_reserved_named(
id: &ReserveIdentifier,
slashed: &AccountId,
beneficiary: &AccountId,
status: BalanceStatus,
) -> DispatchResult {
let value = Self::reserved_balance_named(id, slashed);
Self::repatriate_reserved_named(id, slashed, beneficiary, value, status).map(|_| ())
}
}

/// Handler for account which has dust, need to burn or recycle it
pub trait OnDust<AccountId, CurrencyId, Balance> {
fn on_dust(who: &AccountId, currency_id: CurrencyId, amount: Balance);
Expand Down
2 changes: 1 addition & 1 deletion traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use auction::{Auction, AuctionHandler, AuctionInfo, OnNewBidResult};
pub use currency::{
BalanceStatus, BasicCurrency, BasicCurrencyExtended, BasicLockableCurrency, BasicReservableCurrency,
LockIdentifier, MultiCurrency, MultiCurrencyExtended, MultiLockableCurrency, MultiReservableCurrency,
NamedMultiReservableCurrency, OnDust,
NamedBasicReservableCurrency, NamedMultiReservableCurrency, OnDust,
};
pub use data_provider::{DataFeeder, DataProvider, DataProviderExtended};
pub use get_by_key::GetByKey;
Expand Down