mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 02:17:58 +00:00
Implement fungible::* for Balances (#8454)
* Reservable, Transferrable Fungible(s), plus adapters. * Repot into new dir * Imbalances for Fungibles * Repot and balanced fungible. * Clean up names and bridge-over Imbalanced. * Repot frame_support::trait. Finally. * Make build. * Docs * Good errors * Fix tests. Implement fungible::Inspect for Balances. * Implement additional traits for Balances. * Revert UI test "fixes" * Fix UI error * Fix UI test * More work on fungibles * Fixes * More work. * Update lock * Make fungible::reserved work for Balances * Introduce Freezer to Assets, ready for a reserve & locks pallet. Some renaming/refactoring. * Cleanup errors * Imbalances working with Assets * Test for freezer. * Grumbles * Grumbles * Fixes * Extra "side-car" data for a user's asset balance. * Fix * Fix test * Fixes * Line lengths * Comments * Update frame/assets/src/tests.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/support/src/traits/tokens/fungibles.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/assets/src/lib.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/support/src/traits/tokens/fungible.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Introduce `transfer_reserved` * Rename fungible Reserve -> Hold, add flag structs * Avoid the `melted` API - its too complex and gives little help * Repot Assets pallet Co-authored-by: Bastian Köcher <info@kchr.de> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
@@ -20,15 +20,8 @@
|
||||
//! NOTE: If you're looking for `parameter_types`, it has moved in to the top-level module.
|
||||
|
||||
pub mod tokens;
|
||||
pub use tokens::fungible::{
|
||||
Inspect as InspectFungible, Mutate as MutateFungible, Transfer as TransferFungible,
|
||||
Reserve as ReserveFungible, Balanced as BalancedFungible, Unbalanced as UnbalancedFungible,
|
||||
ItemOf,
|
||||
};
|
||||
pub use tokens::fungibles::{
|
||||
Inspect as InspectFungibles, Mutate as MutateFungibles, Transfer as TransferFungibles,
|
||||
Reserve as ReserveFungibles, Balanced as BalancedFungibles, Unbalanced as UnbalancedFungibles,
|
||||
};
|
||||
pub use tokens::fungible;
|
||||
pub use tokens::fungibles;
|
||||
pub use tokens::currency::{
|
||||
Currency, LockIdentifier, LockableCurrency, ReservableCurrency, VestingSchedule,
|
||||
};
|
||||
|
||||
@@ -32,14 +32,22 @@ pub use imbalance::{Imbalance, HandleImbalanceDrop, DebtOf, CreditOf};
|
||||
pub trait Inspect<AccountId> {
|
||||
/// Scalar type for representing balance of an account.
|
||||
type Balance: Balance;
|
||||
|
||||
/// The total amount of issuance in the system.
|
||||
fn total_issuance() -> Self::Balance;
|
||||
|
||||
/// The minimum balance any single account may have.
|
||||
fn minimum_balance() -> Self::Balance;
|
||||
|
||||
/// Get the balance of `who`.
|
||||
fn balance(who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Get the maximum amount that `who` can withdraw/transfer successfully.
|
||||
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;
|
||||
|
||||
/// Returns `Failed` if the balance of `who` may not be decreased by `amount`, otherwise
|
||||
/// the consequence.
|
||||
fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance>;
|
||||
@@ -47,26 +55,42 @@ pub trait Inspect<AccountId> {
|
||||
|
||||
/// Trait for providing an ERC-20 style fungible asset.
|
||||
pub trait Mutate<AccountId>: Inspect<AccountId> {
|
||||
/// Increase the balance of `who` by `amount`.
|
||||
fn deposit(who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
/// Attempt to reduce the balance of `who` by `amount`.
|
||||
fn withdraw(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError>;
|
||||
/// Transfer funds from one account into another.
|
||||
fn transfer(
|
||||
/// Increase the balance of `who` by exactly `amount`, minting new tokens. If that isn't
|
||||
/// possible then an `Err` is returned and nothing is changed.
|
||||
fn mint_into(who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
|
||||
/// Decrease the balance of `who` by at least `amount`, possibly slightly more in the case of
|
||||
/// minimum_balance requirements, burning the tokens. If that isn't possible then an `Err` is
|
||||
/// returned and nothing is changed. If successful, the amount of tokens reduced is returned.
|
||||
fn burn_from(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError>;
|
||||
|
||||
/// Attempt to reduce the balance of `who` by as much as possible up to `amount`, and possibly
|
||||
/// slightly more due to minimum_balance requirements. If no decrease is possible then an `Err`
|
||||
/// is returned and nothing is changed. If successful, the amount of tokens reduced is returned.
|
||||
///
|
||||
/// The default implementation just uses `withdraw` along with `reducible_balance` to ensure
|
||||
/// that is doesn't fail.
|
||||
fn slash(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
|
||||
Self::burn_from(who, Self::reducible_balance(who, false).min(amount))
|
||||
}
|
||||
|
||||
/// Transfer funds from one account into another. The default implementation uses `mint_into`
|
||||
/// and `burn_from` and may generate unwanted events.
|
||||
fn teleport(
|
||||
source: &AccountId,
|
||||
dest: &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()?;
|
||||
let actual = Self::withdraw(source, amount)?;
|
||||
let actual = Self::burn_from(source, amount)?;
|
||||
debug_assert!(actual == amount.saturating_add(extra), "can_withdraw must agree with withdraw; qed");
|
||||
match Self::deposit(dest, actual) {
|
||||
match Self::mint_into(dest, actual) {
|
||||
Ok(_) => Ok(actual),
|
||||
Err(err) => {
|
||||
debug_assert!(false, "can_deposit returned true previously; qed");
|
||||
// attempt to return the funds back to source
|
||||
let revert = Self::deposit(source, actual);
|
||||
let revert = Self::mint_into(source, actual);
|
||||
debug_assert!(revert.is_ok(), "withdrew funds previously; qed");
|
||||
Err(err)
|
||||
}
|
||||
@@ -81,31 +105,82 @@ pub trait Transfer<AccountId>: Inspect<AccountId> {
|
||||
source: &AccountId,
|
||||
dest: &AccountId,
|
||||
amount: Self::Balance,
|
||||
keep_alive: bool,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
}
|
||||
|
||||
/// Trait for providing a fungible asset which can be reserved.
|
||||
pub trait Reserve<AccountId>: Inspect<AccountId> {
|
||||
/// Trait for inspecting a fungible asset which can be reserved.
|
||||
pub trait InspectHold<AccountId>: Inspect<AccountId> {
|
||||
/// Amount of funds held in reserve by `who`.
|
||||
fn reserved_balance(who: &AccountId) -> Self::Balance;
|
||||
/// Amount of funds held in total by `who`.
|
||||
fn total_balance(who: &AccountId) -> Self::Balance {
|
||||
Self::reserved_balance(who).saturating_add(Self::balance(who))
|
||||
}
|
||||
/// Check to see if some `amount` of funds may be reserved on the account of `who`.
|
||||
fn can_reserve(who: &AccountId, amount: Self::Balance) -> bool;
|
||||
/// Reserve some funds in an account.
|
||||
fn reserve(who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
/// Unreserve some funds in an account.
|
||||
fn unreserve(who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
/// Transfer reserved funds into another account.
|
||||
fn repatriate_reserved(
|
||||
who: &AccountId,
|
||||
amount: Self::Balance,
|
||||
status: BalanceStatus,
|
||||
) -> DispatchResult;
|
||||
fn balance_on_hold(who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Check to see if some `amount` of funds of `who` may be placed on hold.
|
||||
fn can_hold(who: &AccountId, amount: Self::Balance) -> bool;
|
||||
}
|
||||
|
||||
/// Trait for mutating a fungible asset which can be reserved.
|
||||
pub trait MutateHold<AccountId>: InspectHold<AccountId> + Transfer<AccountId> {
|
||||
/// Hold some funds in an account.
|
||||
fn hold(who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
|
||||
/// Release up to `amount` held funds in an account.
|
||||
///
|
||||
/// The actual amount released is returned with `Ok`.
|
||||
///
|
||||
/// If `best_effort` is `true`, then the amount actually unreserved and returned as the inner
|
||||
/// value of `Ok` may be smaller than the `amount` passed.
|
||||
fn release(who: &AccountId, amount: Self::Balance, best_effort: bool)
|
||||
-> Result<Self::Balance, DispatchError>;
|
||||
|
||||
/// Transfer held funds into a destination account.
|
||||
///
|
||||
/// If `on_hold` is `true`, then the destination account must already exist and the assets
|
||||
/// transferred will still be on hold in the destination account. If not, then the destination
|
||||
/// account need not already exist, but must be creatable.
|
||||
///
|
||||
/// If `best_effort` is `true`, then an amount less than `amount` may be transferred without
|
||||
/// error.
|
||||
///
|
||||
/// The actual amount transferred is returned, or `Err` in the case of error and nothing is
|
||||
/// changed.
|
||||
fn transfer_held(
|
||||
source: &AccountId,
|
||||
dest: &AccountId,
|
||||
amount: Self::Balance,
|
||||
best_effort: bool,
|
||||
on_held: bool,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
}
|
||||
|
||||
/// Trait for slashing a fungible asset which can be reserved.
|
||||
pub trait BalancedHold<AccountId>: Balanced<AccountId> + MutateHold<AccountId> {
|
||||
/// Reduce the balance of some funds on hold in an account.
|
||||
///
|
||||
/// The resulting imbalance is the first item of the tuple returned.
|
||||
///
|
||||
/// As much funds that are on hold up to `amount` will be deducted as possible. If this is less
|
||||
/// than `amount`, then a non-zero second item will be returned.
|
||||
fn slash_held(who: &AccountId, amount: Self::Balance)
|
||||
-> (CreditOf<AccountId, Self>, Self::Balance);
|
||||
}
|
||||
|
||||
impl<
|
||||
AccountId,
|
||||
T: Balanced<AccountId> + MutateHold<AccountId>,
|
||||
> BalancedHold<AccountId> for T {
|
||||
fn slash_held(who: &AccountId, amount: Self::Balance)
|
||||
-> (CreditOf<AccountId, Self>, Self::Balance)
|
||||
{
|
||||
let actual = match Self::release(who, amount, true) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return (Imbalance::default(), amount),
|
||||
};
|
||||
<Self as fungible::Balanced<AccountId>>::slash(who, actual)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a `fungibles` trait implementation into a `fungible` trait implementation by identifying
|
||||
/// a single item.
|
||||
pub struct ItemOf<
|
||||
F: fungibles::Inspect<AccountId>,
|
||||
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
|
||||
@@ -129,6 +204,9 @@ impl<
|
||||
fn balance(who: &AccountId) -> Self::Balance {
|
||||
<F as fungibles::Inspect<AccountId>>::balance(A::get(), who)
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -142,11 +220,11 @@ impl<
|
||||
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
|
||||
AccountId,
|
||||
> Mutate<AccountId> for ItemOf<F, A, AccountId> {
|
||||
fn deposit(who: &AccountId, amount: Self::Balance) -> DispatchResult {
|
||||
<F as fungibles::Mutate<AccountId>>::deposit(A::get(), who, amount)
|
||||
fn mint_into(who: &AccountId, amount: Self::Balance) -> DispatchResult {
|
||||
<F as fungibles::Mutate<AccountId>>::mint_into(A::get(), who, amount)
|
||||
}
|
||||
fn withdraw(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
|
||||
<F as fungibles::Mutate<AccountId>>::withdraw(A::get(), who, amount)
|
||||
fn burn_from(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
|
||||
<F as fungibles::Mutate<AccountId>>::burn_from(A::get(), who, amount)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,39 +233,54 @@ impl<
|
||||
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
|
||||
AccountId,
|
||||
> Transfer<AccountId> for ItemOf<F, A, AccountId> {
|
||||
fn transfer(source: &AccountId, dest: &AccountId, amount: Self::Balance)
|
||||
fn transfer(source: &AccountId, dest: &AccountId, amount: Self::Balance, keep_alive: bool)
|
||||
-> Result<Self::Balance, DispatchError>
|
||||
{
|
||||
<F as fungibles::Transfer<AccountId>>::transfer(A::get(), source, dest, amount)
|
||||
<F as fungibles::Transfer<AccountId>>::transfer(A::get(), source, dest, amount, keep_alive)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
F: fungibles::Reserve<AccountId>,
|
||||
F: fungibles::InspectHold<AccountId>,
|
||||
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
|
||||
AccountId,
|
||||
> Reserve<AccountId> for ItemOf<F, A, AccountId> {
|
||||
fn reserved_balance(who: &AccountId) -> Self::Balance {
|
||||
<F as fungibles::Reserve<AccountId>>::reserved_balance(A::get(), who)
|
||||
> InspectHold<AccountId> for ItemOf<F, A, AccountId> {
|
||||
fn balance_on_hold(who: &AccountId) -> Self::Balance {
|
||||
<F as fungibles::InspectHold<AccountId>>::balance_on_hold(A::get(), who)
|
||||
}
|
||||
fn total_balance(who: &AccountId) -> Self::Balance {
|
||||
<F as fungibles::Reserve<AccountId>>::total_balance(A::get(), who)
|
||||
fn can_hold(who: &AccountId, amount: Self::Balance) -> bool {
|
||||
<F as fungibles::InspectHold<AccountId>>::can_hold(A::get(), who, amount)
|
||||
}
|
||||
fn can_reserve(who: &AccountId, amount: Self::Balance) -> bool {
|
||||
<F as fungibles::Reserve<AccountId>>::can_reserve(A::get(), who, amount)
|
||||
}
|
||||
|
||||
impl<
|
||||
F: fungibles::MutateHold<AccountId>,
|
||||
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
|
||||
AccountId,
|
||||
> MutateHold<AccountId> for ItemOf<F, A, AccountId> {
|
||||
fn hold(who: &AccountId, amount: Self::Balance) -> DispatchResult {
|
||||
<F as fungibles::MutateHold<AccountId>>::hold(A::get(), who, amount)
|
||||
}
|
||||
fn reserve(who: &AccountId, amount: Self::Balance) -> DispatchResult {
|
||||
<F as fungibles::Reserve<AccountId>>::reserve(A::get(), who, amount)
|
||||
fn release(who: &AccountId, amount: Self::Balance, best_effort: bool)
|
||||
-> Result<Self::Balance, DispatchError>
|
||||
{
|
||||
<F as fungibles::MutateHold<AccountId>>::release(A::get(), who, amount, best_effort)
|
||||
}
|
||||
fn unreserve(who: &AccountId, amount: Self::Balance) -> DispatchResult {
|
||||
<F as fungibles::Reserve<AccountId>>::unreserve(A::get(), who, amount)
|
||||
}
|
||||
fn repatriate_reserved(
|
||||
who: &AccountId,
|
||||
fn transfer_held(
|
||||
source: &AccountId,
|
||||
dest: &AccountId,
|
||||
amount: Self::Balance,
|
||||
status: BalanceStatus,
|
||||
) -> DispatchResult {
|
||||
<F as fungibles::Reserve<AccountId>>::repatriate_reserved(A::get(), who, amount, status)
|
||||
best_effort: bool,
|
||||
on_hold: bool,
|
||||
) -> Result<Self::Balance, DispatchError> {
|
||||
<F as fungibles::MutateHold<AccountId>>::transfer_held(
|
||||
A::get(),
|
||||
source,
|
||||
dest,
|
||||
amount,
|
||||
best_effort,
|
||||
on_hold,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,4 +308,3 @@ impl<
|
||||
<F as fungibles::Unbalanced<AccountId>>::increase_balance_at_most(A::get(), who, amount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,14 +55,11 @@ pub trait Balanced<AccountId>: Inspect<AccountId> {
|
||||
///
|
||||
/// This is just the same as burning and issuing the same amount and has no effect on the
|
||||
/// total issuance.
|
||||
fn pair(amount: Self::Balance)
|
||||
-> (DebtOf<AccountId, Self>, CreditOf<AccountId, Self>)
|
||||
{
|
||||
fn pair(amount: Self::Balance) -> (DebtOf<AccountId, Self>, CreditOf<AccountId, Self>) {
|
||||
(Self::rescind(amount), Self::issue(amount))
|
||||
}
|
||||
|
||||
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
|
||||
/// free balance. This function cannot fail.
|
||||
/// Deducts up to `value` from the combined balance of `who`. This function cannot fail.
|
||||
///
|
||||
/// The resulting imbalance is the first item of the tuple returned.
|
||||
///
|
||||
|
||||
@@ -31,17 +31,26 @@ pub use imbalance::{Imbalance, HandleImbalanceDrop, DebtOf, CreditOf};
|
||||
pub trait Inspect<AccountId> {
|
||||
/// Means of identifying one asset class from another.
|
||||
type AssetId: AssetId;
|
||||
|
||||
/// Scalar type for representing balance of an account.
|
||||
type Balance: Balance;
|
||||
|
||||
/// The total amount of issuance in the system.
|
||||
fn total_issuance(asset: Self::AssetId) -> Self::Balance;
|
||||
|
||||
/// The minimum balance any single account may have.
|
||||
fn minimum_balance(asset: Self::AssetId) -> Self::Balance;
|
||||
|
||||
/// Get the `asset` balance of `who`.
|
||||
fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Get the maximum amount of `asset` that `who` can withdraw/transfer successfully.
|
||||
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`.
|
||||
fn can_deposit(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
|
||||
-> DepositConsequence;
|
||||
|
||||
/// Returns `Failed` if the `asset` balance of `who` may not be decreased by `amount`, otherwise
|
||||
/// the consequence.
|
||||
fn can_withdraw(
|
||||
@@ -62,7 +71,7 @@ pub trait Mutate<AccountId>: Inspect<AccountId> {
|
||||
///
|
||||
/// Since this is an operation which should be possible to take alone, if successful it will
|
||||
/// increase the overall supply of the underlying token.
|
||||
fn deposit(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
fn mint_into(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
|
||||
/// Attempt to reduce the `asset` balance of `who` by `amount`.
|
||||
///
|
||||
@@ -78,11 +87,25 @@ pub trait Mutate<AccountId>: Inspect<AccountId> {
|
||||
/// Due to minimum balance requirements, it's possible that the amount withdrawn could be up to
|
||||
/// `Self::minimum_balance() - 1` more than the `amount`. The total amount withdrawn is returned
|
||||
/// in an `Ok` result. This may be safely ignored if you don't mind the overall supply reducing.
|
||||
fn withdraw(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
|
||||
fn burn_from(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
|
||||
-> Result<Self::Balance, DispatchError>;
|
||||
|
||||
/// Transfer funds from one account into another.
|
||||
fn transfer(
|
||||
/// Attempt to reduce the `asset` balance of `who` by as much as possible up to `amount`, and
|
||||
/// possibly slightly more due to minimum_balance requirements. If no decrease is possible then
|
||||
/// an `Err` is returned and nothing is changed. If successful, the amount of tokens reduced is
|
||||
/// returned.
|
||||
///
|
||||
/// The default implementation just uses `withdraw` along with `reducible_balance` to ensure
|
||||
/// that is doesn't fail.
|
||||
fn slash(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
|
||||
-> Result<Self::Balance, DispatchError>
|
||||
{
|
||||
Self::burn_from(asset, who, Self::reducible_balance(asset, who, false).min(amount))
|
||||
}
|
||||
|
||||
/// Transfer funds from one account into another. The default implementation uses `mint_into`
|
||||
/// and `burn_from` and may generate unwanted events.
|
||||
fn teleport(
|
||||
asset: Self::AssetId,
|
||||
source: &AccountId,
|
||||
dest: &AccountId,
|
||||
@@ -90,14 +113,14 @@ pub trait Mutate<AccountId>: Inspect<AccountId> {
|
||||
) -> 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()?;
|
||||
let actual = Self::withdraw(asset, source, amount)?;
|
||||
let actual = Self::burn_from(asset, source, amount)?;
|
||||
debug_assert!(actual == amount.saturating_add(extra), "can_withdraw must agree with withdraw; qed");
|
||||
match Self::deposit(asset, dest, actual) {
|
||||
match Self::mint_into(asset, dest, actual) {
|
||||
Ok(_) => Ok(actual),
|
||||
Err(err) => {
|
||||
debug_assert!(false, "can_deposit returned true previously; qed");
|
||||
// attempt to return the funds back to source
|
||||
let revert = Self::deposit(asset, source, actual);
|
||||
let revert = Self::mint_into(asset, source, actual);
|
||||
debug_assert!(revert.is_ok(), "withdrew funds previously; qed");
|
||||
Err(err)
|
||||
}
|
||||
@@ -113,31 +136,75 @@ pub trait Transfer<AccountId>: Inspect<AccountId> {
|
||||
source: &AccountId,
|
||||
dest: &AccountId,
|
||||
amount: Self::Balance,
|
||||
keep_alive: bool,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
}
|
||||
|
||||
/// Trait for providing a set of named fungible assets which can be reserved.
|
||||
pub trait Reserve<AccountId>: Inspect<AccountId> {
|
||||
/// Amount of funds held in reserve.
|
||||
fn reserved_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance;
|
||||
/// Trait for inspecting a set of named fungible assets which can be placed on hold.
|
||||
pub trait InspectHold<AccountId>: Inspect<AccountId> {
|
||||
/// Amount of funds held in hold.
|
||||
fn balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Amount of funds held in reserve.
|
||||
fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Check to see if some `amount` of `asset` may be reserved on the account of `who`.
|
||||
fn can_reserve(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> bool;
|
||||
|
||||
/// Reserve some funds in an account.
|
||||
fn reserve(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
|
||||
/// Unreserve some funds in an account.
|
||||
fn unreserve(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
|
||||
/// Transfer reserved funds into another account.
|
||||
fn repatriate_reserved(
|
||||
asset: Self::AssetId,
|
||||
who: &AccountId,
|
||||
amount: Self::Balance,
|
||||
status: BalanceStatus,
|
||||
) -> DispatchResult;
|
||||
/// Check to see if some `amount` of `asset` may be held on the account of `who`.
|
||||
fn can_hold(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> bool;
|
||||
}
|
||||
|
||||
/// Trait for mutating a set of named fungible assets which can be placed on hold.
|
||||
pub trait MutateHold<AccountId>: InspectHold<AccountId> + Transfer<AccountId> {
|
||||
/// Hold some funds in an account.
|
||||
fn hold(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult;
|
||||
|
||||
/// Release some funds in an account from being on hold.
|
||||
///
|
||||
/// If `best_effort` is `true`, then the amount actually released and returned as the inner
|
||||
/// value of `Ok` may be smaller than the `amount` passed.
|
||||
fn release(asset: Self::AssetId, who: &AccountId, amount: Self::Balance, best_effort: bool)
|
||||
-> Result<Self::Balance, DispatchError>;
|
||||
|
||||
/// Transfer held funds into a destination account.
|
||||
///
|
||||
/// If `on_hold` is `true`, then the destination account must already exist and the assets
|
||||
/// transferred will still be on hold in the destination account. If not, then the destination
|
||||
/// account need not already exist, but must be creatable.
|
||||
///
|
||||
/// If `best_effort` is `true`, then an amount less than `amount` may be transferred without
|
||||
/// error.
|
||||
///
|
||||
/// The actual amount transferred is returned, or `Err` in the case of error and nothing is
|
||||
/// changed.
|
||||
fn transfer_held(
|
||||
asset: Self::AssetId,
|
||||
source: &AccountId,
|
||||
dest: &AccountId,
|
||||
amount: Self::Balance,
|
||||
best_effort: bool,
|
||||
on_hold: bool,
|
||||
) -> Result<Self::Balance, DispatchError>;
|
||||
}
|
||||
|
||||
/// Trait for mutating one of several types of fungible assets which can be held.
|
||||
pub trait BalancedHold<AccountId>: Balanced<AccountId> + MutateHold<AccountId> {
|
||||
/// Release and slash some funds in an account.
|
||||
///
|
||||
/// The resulting imbalance is the first item of the tuple returned.
|
||||
///
|
||||
/// As much funds up to `amount` will be deducted as possible. If this is less than `amount`,
|
||||
/// then a non-zero second item will be returned.
|
||||
fn slash_held(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
|
||||
-> (CreditOf<AccountId, Self>, Self::Balance);
|
||||
}
|
||||
|
||||
impl<
|
||||
AccountId,
|
||||
T: Balanced<AccountId> + MutateHold<AccountId>,
|
||||
> BalancedHold<AccountId> for T {
|
||||
fn slash_held(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
|
||||
-> (CreditOf<AccountId, Self>, Self::Balance)
|
||||
{
|
||||
let actual = match Self::release(asset, who, amount, true) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return (Imbalance::zero(asset), amount),
|
||||
};
|
||||
<Self as fungibles::Balanced<AccountId>>::slash(asset, who, actual)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,10 @@ use crate::traits::misc::{SameOrOther, TryDrop};
|
||||
///
|
||||
/// This is auto-implemented when a token class has `Unbalanced` implemented.
|
||||
pub trait Balanced<AccountId>: Inspect<AccountId> {
|
||||
/// The type for managing what happens when an instance of `Debt` is dropped without being used.
|
||||
type OnDropDebt: HandleImbalanceDrop<Self::AssetId, Self::Balance>;
|
||||
/// The type for managing what happens when an instance of `Credit` is dropped without being
|
||||
/// used.
|
||||
type OnDropCredit: HandleImbalanceDrop<Self::AssetId, Self::Balance>;
|
||||
|
||||
/// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will
|
||||
|
||||
@@ -37,6 +37,9 @@ pub enum WithdrawConsequence<Balance> {
|
||||
/// There has been an underflow in the system. This is indicative of a corrupt state and
|
||||
/// likely unrecoverable.
|
||||
Underflow,
|
||||
/// There has been an overflow in the system. This is indicative of a corrupt state and
|
||||
/// likely unrecoverable.
|
||||
Overflow,
|
||||
/// Not enough of the funds in the account are unavailable for withdrawal.
|
||||
Frozen,
|
||||
/// Account balance would reduce to zero, potentially destroying it. The parameter is the
|
||||
@@ -56,6 +59,7 @@ impl<Balance: Zero> WithdrawConsequence<Balance> {
|
||||
WouldDie => Err(TokenError::WouldDie),
|
||||
UnknownAsset => Err(TokenError::UnknownAsset),
|
||||
Underflow => Err(TokenError::Underflow),
|
||||
Overflow => Err(TokenError::Overflow),
|
||||
Frozen => Err(TokenError::Frozen),
|
||||
ReducedToZero(result) => Ok(result),
|
||||
Success => Ok(Zero::zero()),
|
||||
|
||||
Reference in New Issue
Block a user