Repot frame_support::traits; introduce some new currency stuff (#8435)

* 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

* Fixes

* Update lock

* Grumbles

* Grumbles

* Fixes

Co-authored-by: Bastian Köcher <info@kchr.de>
This commit is contained in:
Gavin Wood
2021-03-27 14:37:13 +01:00
committed by GitHub
parent 5d2640240c
commit ff5765eac3
34 changed files with 4748 additions and 2372 deletions
@@ -0,0 +1,208 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The Currency trait and associated types.
use sp_std::fmt::Debug;
use sp_runtime::traits::MaybeSerializeDeserialize;
use crate::dispatch::{DispatchResult, DispatchError};
use super::misc::{Balance, WithdrawReasons, ExistenceRequirement};
use super::imbalance::{Imbalance, SignedImbalance};
mod reservable;
pub use reservable::ReservableCurrency;
mod lockable;
pub use lockable::{LockableCurrency, VestingSchedule, LockIdentifier};
/// Abstraction over a fungible assets system.
pub trait Currency<AccountId> {
/// The balance of an account.
type Balance: Balance + MaybeSerializeDeserialize + Debug;
/// The opaque token type for an imbalance. This is returned by unbalanced operations
/// and must be dealt with. It may be dropped but cannot be cloned.
type PositiveImbalance: Imbalance<Self::Balance, Opposite=Self::NegativeImbalance>;
/// The opaque token type for an imbalance. This is returned by unbalanced operations
/// and must be dealt with. It may be dropped but cannot be cloned.
type NegativeImbalance: Imbalance<Self::Balance, Opposite=Self::PositiveImbalance>;
// PUBLIC IMMUTABLES
/// The combined balance of `who`.
fn total_balance(who: &AccountId) -> Self::Balance;
/// Same result as `slash(who, value)` (but without the side-effects) assuming there are no
/// balance changes in the meantime and only the reserved balance is not taken into account.
fn can_slash(who: &AccountId, value: Self::Balance) -> bool;
/// The total amount of issuance in the system.
fn total_issuance() -> Self::Balance;
/// The minimum balance any single account may have. This is equivalent to the `Balances` module's
/// `ExistentialDeposit`.
fn minimum_balance() -> Self::Balance;
/// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will
/// typically be used to reduce an account by the same amount with e.g. `settle`.
///
/// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example
/// in the case of underflow.
fn burn(amount: Self::Balance) -> Self::PositiveImbalance;
/// Increase the total issuance by `amount` and return the according imbalance. The imbalance
/// will typically be used to increase an account by the same amount with e.g.
/// `resolve_into_existing` or `resolve_creating`.
///
/// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example
/// in the case of overflow.
fn issue(amount: Self::Balance) -> Self::NegativeImbalance;
/// Produce a pair of imbalances that cancel each other out exactly.
///
/// 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) -> (Self::PositiveImbalance, Self::NegativeImbalance) {
(Self::burn(amount.clone()), Self::issue(amount))
}
/// The 'free' balance of a given account.
///
/// This is the only balance that matters in terms of most operations on tokens. It alone
/// is used to determine the balance when in the contract execution environment. When this
/// balance falls below the value of `ExistentialDeposit`, then the 'current account' is
/// deleted: specifically `FreeBalance`.
///
/// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
fn free_balance(who: &AccountId) -> Self::Balance;
/// Returns `Ok` iff the account is able to make a withdrawal of the given amount
/// for the given reason. Basically, it's just a dry-run of `withdraw`.
///
/// `Err(...)` with the reason why not otherwise.
fn ensure_can_withdraw(
who: &AccountId,
_amount: Self::Balance,
reasons: WithdrawReasons,
new_balance: Self::Balance,
) -> DispatchResult;
// PUBLIC MUTABLES (DANGEROUS)
/// Transfer some liquid free balance to another staker.
///
/// This is a very high-level function. It will ensure all appropriate fees are paid
/// and no imbalance in the system remains.
fn transfer(
source: &AccountId,
dest: &AccountId,
value: Self::Balance,
existence_requirement: ExistenceRequirement,
) -> DispatchResult;
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
/// free balance. This function cannot fail.
///
/// The resulting imbalance is the first item of the tuple returned.
///
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
/// then a non-zero second item will be returned.
fn slash(
who: &AccountId,
value: Self::Balance
) -> (Self::NegativeImbalance, Self::Balance);
/// Mints `value` to the free balance of `who`.
///
/// If `who` doesn't exist, nothing is done and an Err returned.
fn deposit_into_existing(
who: &AccountId,
value: Self::Balance
) -> Result<Self::PositiveImbalance, DispatchError>;
/// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on
/// success.
fn resolve_into_existing(
who: &AccountId,
value: Self::NegativeImbalance,
) -> Result<(), Self::NegativeImbalance> {
let v = value.peek();
match Self::deposit_into_existing(who, v) {
Ok(opposite) => Ok(drop(value.offset(opposite))),
_ => Err(value),
}
}
/// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created.
///
/// Infallible.
fn deposit_creating(
who: &AccountId,
value: Self::Balance,
) -> Self::PositiveImbalance;
/// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on
/// success.
fn resolve_creating(
who: &AccountId,
value: Self::NegativeImbalance,
) {
let v = value.peek();
drop(value.offset(Self::deposit_creating(who, v)));
}
/// Removes some free balance from `who` account for `reason` if possible. If `liveness` is
/// `KeepAlive`, then no less than `ExistentialDeposit` must be left remaining.
///
/// This checks any locks, vesting, and liquidity requirements. If the removal is not possible,
/// then it returns `Err`.
///
/// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value
/// is `value`.
fn withdraw(
who: &AccountId,
value: Self::Balance,
reasons: WithdrawReasons,
liveness: ExistenceRequirement,
) -> Result<Self::NegativeImbalance, DispatchError>;
/// Similar to withdraw, only accepts a `PositiveImbalance` and returns nothing on success.
fn settle(
who: &AccountId,
value: Self::PositiveImbalance,
reasons: WithdrawReasons,
liveness: ExistenceRequirement,
) -> Result<(), Self::PositiveImbalance> {
let v = value.peek();
match Self::withdraw(who, v, reasons, liveness) {
Ok(opposite) => Ok(drop(value.offset(opposite))),
_ => Err(value),
}
}
/// Ensure an account's free balance equals some value; this will create the account
/// if needed.
///
/// Returns a signed imbalance and status to indicate if the account was successfully updated or update
/// has led to killing of the account.
fn make_free_balance_be(
who: &AccountId,
balance: Self::Balance,
) -> SignedImbalance<Self::Balance, Self::PositiveImbalance>;
}
@@ -0,0 +1,104 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The lockable currency trait and some associated types.
use crate::dispatch::DispatchResult;
use crate::traits::misc::Get;
use super::Currency;
use super::super::misc::WithdrawReasons;
/// An identifier for a lock. Used for disambiguating different locks so that
/// they can be individually replaced or removed.
pub type LockIdentifier = [u8; 8];
/// A currency whose accounts can have liquidity restrictions.
pub trait LockableCurrency<AccountId>: Currency<AccountId> {
/// The quantity used to denote time; usually just a `BlockNumber`.
type Moment;
/// The maximum number of locks a user should have on their account.
type MaxLocks: Get<u32>;
/// Create a new balance lock on account `who`.
///
/// If the new lock is valid (i.e. not already expired), it will push the struct to
/// the `Locks` vec in storage. Note that you can lock more funds than a user has.
///
/// If the lock `id` already exists, this will update it.
fn set_lock(
id: LockIdentifier,
who: &AccountId,
amount: Self::Balance,
reasons: WithdrawReasons,
);
/// Changes a balance lock (selected by `id`) so that it becomes less liquid in all
/// parameters or creates a new one if it does not exist.
///
/// Calling `extend_lock` on an existing lock `id` differs from `set_lock` in that it
/// applies the most severe constraints of the two, while `set_lock` replaces the lock
/// with the new parameters. As in, `extend_lock` will set:
/// - maximum `amount`
/// - bitwise mask of all `reasons`
fn extend_lock(
id: LockIdentifier,
who: &AccountId,
amount: Self::Balance,
reasons: WithdrawReasons,
);
/// Remove an existing lock.
fn remove_lock(
id: LockIdentifier,
who: &AccountId,
);
}
/// A vesting schedule over a currency. This allows a particular currency to have vesting limits
/// applied to it.
pub trait VestingSchedule<AccountId> {
/// The quantity used to denote time; usually just a `BlockNumber`.
type Moment;
/// The currency that this schedule applies to.
type Currency: Currency<AccountId>;
/// Get the amount that is currently being vested and cannot be transferred out of this account.
/// Returns `None` if the account has no vesting schedule.
fn vesting_balance(who: &AccountId) -> Option<<Self::Currency as Currency<AccountId>>::Balance>;
/// Adds a vesting schedule to a given account.
///
/// If there already exists a vesting schedule for the given account, an `Err` is returned
/// and nothing is updated.
///
/// Is a no-op if the amount to be vested is zero.
///
/// NOTE: This doesn't alter the free balance of the account.
fn add_vesting_schedule(
who: &AccountId,
locked: <Self::Currency as Currency<AccountId>>::Balance,
per_block: <Self::Currency as Currency<AccountId>>::Balance,
starting_block: Self::Moment,
) -> DispatchResult;
/// Remove a vesting schedule for a given account.
///
/// NOTE: This doesn't alter the free balance of the account.
fn remove_vesting_schedule(who: &AccountId);
}
@@ -0,0 +1,83 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The reservable currency trait.
use super::Currency;
use super::super::misc::BalanceStatus;
use crate::dispatch::{DispatchResult, DispatchError};
/// A currency where funds can be reserved from the user.
pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
/// Same result as `reserve(who, value)` (but without the side-effects) assuming there
/// are no balance changes in the meantime.
fn can_reserve(who: &AccountId, value: Self::Balance) -> bool;
/// 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 second item will be returned.
fn slash_reserved(
who: &AccountId,
value: Self::Balance
) -> (Self::NegativeImbalance, 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(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(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(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(
slashed: &AccountId,
beneficiary: &AccountId,
value: Self::Balance,
status: BalanceStatus,
) -> Result<Self::Balance, DispatchError>;
}
@@ -0,0 +1,218 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The traits for dealing with a single fungible token class and any associated types.
use super::*;
use sp_runtime::traits::Saturating;
use crate::traits::misc::Get;
use crate::dispatch::{DispatchResult, DispatchError};
use super::misc::{DepositConsequence, WithdrawConsequence, Balance};
mod balanced;
mod imbalance;
pub use balanced::{Balanced, Unbalanced};
pub use imbalance::{Imbalance, HandleImbalanceDrop, DebtOf, CreditOf};
/// Trait for providing balance-inspection access to a fungible asset.
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;
/// 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>;
}
/// 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(
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)?;
debug_assert!(actual == amount.saturating_add(extra), "can_withdraw must agree with withdraw; qed");
match Self::deposit(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);
debug_assert!(revert.is_ok(), "withdrew funds previously; qed");
Err(err)
}
}
}
}
/// Trait for providing a fungible asset which can only be transferred.
pub trait Transfer<AccountId>: Inspect<AccountId> {
/// Transfer funds from one account into another.
fn transfer(
source: &AccountId,
dest: &AccountId,
amount: Self::Balance,
) -> Result<Self::Balance, DispatchError>;
}
/// Trait for providing a fungible asset which can be reserved.
pub trait Reserve<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;
}
pub struct ItemOf<
F: fungibles::Inspect<AccountId>,
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
AccountId,
>(
sp_std::marker::PhantomData<(F, A, AccountId)>
);
impl<
F: fungibles::Inspect<AccountId>,
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
AccountId,
> Inspect<AccountId> for ItemOf<F, A, AccountId> {
type Balance = <F as fungibles::Inspect<AccountId>>::Balance;
fn total_issuance() -> Self::Balance {
<F as fungibles::Inspect<AccountId>>::total_issuance(A::get())
}
fn minimum_balance() -> Self::Balance {
<F as fungibles::Inspect<AccountId>>::minimum_balance(A::get())
}
fn balance(who: &AccountId) -> Self::Balance {
<F as fungibles::Inspect<AccountId>>::balance(A::get(), who)
}
fn can_deposit(who: &AccountId, amount: Self::Balance) -> DepositConsequence {
<F as fungibles::Inspect<AccountId>>::can_deposit(A::get(), who, amount)
}
fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance> {
<F as fungibles::Inspect<AccountId>>::can_withdraw(A::get(), who, amount)
}
}
impl<
F: fungibles::Mutate<AccountId>,
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 withdraw(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
<F as fungibles::Mutate<AccountId>>::withdraw(A::get(), who, amount)
}
}
impl<
F: fungibles::Transfer<AccountId>,
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)
-> Result<Self::Balance, DispatchError>
{
<F as fungibles::Transfer<AccountId>>::transfer(A::get(), source, dest, amount)
}
}
impl<
F: fungibles::Reserve<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)
}
fn total_balance(who: &AccountId) -> Self::Balance {
<F as fungibles::Reserve<AccountId>>::total_balance(A::get(), who)
}
fn can_reserve(who: &AccountId, amount: Self::Balance) -> bool {
<F as fungibles::Reserve<AccountId>>::can_reserve(A::get(), who, amount)
}
fn reserve(who: &AccountId, amount: Self::Balance) -> DispatchResult {
<F as fungibles::Reserve<AccountId>>::reserve(A::get(), who, amount)
}
fn unreserve(who: &AccountId, amount: Self::Balance) -> DispatchResult {
<F as fungibles::Reserve<AccountId>>::unreserve(A::get(), who, amount)
}
fn repatriate_reserved(
who: &AccountId,
amount: Self::Balance,
status: BalanceStatus,
) -> DispatchResult {
<F as fungibles::Reserve<AccountId>>::repatriate_reserved(A::get(), who, amount, status)
}
}
impl<
F: fungibles::Unbalanced<AccountId>,
A: Get<<F as fungibles::Inspect<AccountId>>::AssetId>,
AccountId,
> Unbalanced<AccountId> for ItemOf<F, A, AccountId> {
fn set_balance(who: &AccountId, amount: Self::Balance) -> DispatchResult {
<F as fungibles::Unbalanced<AccountId>>::set_balance(A::get(), who, amount)
}
fn set_total_issuance(amount: Self::Balance) -> () {
<F as fungibles::Unbalanced<AccountId>>::set_total_issuance(A::get(), amount)
}
fn decrease_balance(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
<F as fungibles::Unbalanced<AccountId>>::decrease_balance(A::get(), who, amount)
}
fn decrease_balance_at_most(who: &AccountId, amount: Self::Balance) -> Self::Balance {
<F as fungibles::Unbalanced<AccountId>>::decrease_balance_at_most(A::get(), who, amount)
}
fn increase_balance(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
<F as fungibles::Unbalanced<AccountId>>::increase_balance(A::get(), who, amount)
}
fn increase_balance_at_most(who: &AccountId, amount: Self::Balance) -> Self::Balance {
<F as fungibles::Unbalanced<AccountId>>::increase_balance_at_most(A::get(), who, amount)
}
}
@@ -0,0 +1,363 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The trait and associated types for sets of fungible tokens that manage total issuance without
//! requiring atomic balanced operations.
use super::*;
use sp_std::marker::PhantomData;
use sp_runtime::{TokenError, traits::{CheckedAdd, Zero}};
use super::super::Imbalance as ImbalanceT;
use crate::traits::misc::{SameOrOther, TryDrop};
use crate::dispatch::{DispatchResult, DispatchError};
/// A fungible token class where any creation and deletion of tokens is semi-explicit and where the
/// total supply is maintained automatically.
///
/// 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::Balance>;
/// The type for managing what happens when an instance of `Credit` is dropped without being
/// used.
type OnDropCredit: HandleImbalanceDrop<Self::Balance>;
/// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will
/// typically be used to reduce an account by the same amount with e.g. `settle`.
///
/// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example
/// in the case of underflow.
fn rescind(amount: Self::Balance) -> DebtOf<AccountId, Self>;
/// Increase the total issuance by `amount` and return the according imbalance. The imbalance
/// will typically be used to increase an account by the same amount with e.g.
/// `resolve_into_existing` or `resolve_creating`.
///
/// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example
/// in the case of overflow.
fn issue(amount: Self::Balance) -> CreditOf<AccountId, Self>;
/// Produce a pair of imbalances that cancel each other out exactly.
///
/// 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>)
{
(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.
///
/// The resulting imbalance is the first item of the tuple returned.
///
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
/// then a non-zero second item will be returned.
fn slash(
who: &AccountId,
amount: Self::Balance,
) -> (CreditOf<AccountId, Self>, Self::Balance);
/// Mints exactly `value` into the account of `who`.
///
/// If `who` doesn't exist, nothing is done and an `Err` returned. This could happen because it
/// the account doesn't yet exist and it isn't possible to create it under the current
/// circumstances and with `value` in it.
fn deposit(
who: &AccountId,
value: Self::Balance,
) -> Result<DebtOf<AccountId, Self>, DispatchError>;
/// Removes `value` balance from `who` account if possible.
///
/// If the removal is not possible, then it returns `Err` and nothing is changed.
///
/// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value
/// is no less than `value`. It may be more in the case that removing it reduced it below
/// `Self::minimum_balance()`.
fn withdraw(
who: &AccountId,
value: Self::Balance,
//TODO: liveness: ExistenceRequirement,
) -> Result<CreditOf<AccountId, Self>, DispatchError>;
/// The balance of `who` is increased in order to counter `credit`. If the whole of `credit`
/// cannot be countered, then nothing is changed and the original `credit` is returned in an
/// `Err`.
///
/// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must
/// already exist for this to succeed.
fn resolve(
who: &AccountId,
credit: CreditOf<AccountId, Self>,
) -> Result<(), CreditOf<AccountId, Self>> {
let v = credit.peek();
let debt = match Self::deposit(who, v) {
Err(_) => return Err(credit),
Ok(d) => d,
};
let result = credit.offset(debt).try_drop();
debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed");
Ok(())
}
/// The balance of `who` is decreased in order to counter `debt`. If the whole of `debt`
/// cannot be countered, then nothing is changed and the original `debt` is returned in an
/// `Err`.
fn settle(
who: &AccountId,
debt: DebtOf<AccountId, Self>,
//TODO: liveness: ExistenceRequirement,
) -> Result<CreditOf<AccountId, Self>, DebtOf<AccountId, Self>> {
let amount = debt.peek();
let credit = match Self::withdraw(who, amount) {
Err(_) => return Err(debt),
Ok(d) => d,
};
match credit.offset(debt) {
SameOrOther::None => Ok(CreditOf::<AccountId, Self>::zero()),
SameOrOther::Same(dust) => Ok(dust),
SameOrOther::Other(rest) => {
debug_assert!(false, "ok withdraw return must be at least debt value; qed");
Err(rest)
}
}
}
}
/// A fungible token class where the balance can be set arbitrarily.
///
/// **WARNING**
/// Do not use this directly unless you want trouble, since it allows you to alter account balances
/// without keeping the issuance up to date. It has no safeguards against accidentally creating
/// token imbalances in your system leading to accidental imflation or deflation. It's really just
/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to
/// use.
pub trait Unbalanced<AccountId>: Inspect<AccountId> {
/// Set the balance of `who` to `amount`. If this cannot be done for some reason (e.g.
/// because the account cannot be created or an overflow) then an `Err` is returned.
fn set_balance(who: &AccountId, amount: Self::Balance) -> DispatchResult;
/// Set the total issuance to `amount`.
fn set_total_issuance(amount: Self::Balance);
/// Reduce the balance of `who` by `amount`. If it cannot be reduced by that amount for
/// some reason, return `Err` and don't reduce it at all. If Ok, return the imbalance.
///
/// Minimum balance will be respected and the returned imbalance may be up to
/// `Self::minimum_balance() - 1` greater than `amount`.
fn decrease_balance(who: &AccountId, amount: Self::Balance)
-> Result<Self::Balance, DispatchError>
{
let old_balance = Self::balance(who);
let (mut new_balance, mut amount) = if old_balance < amount {
Err(TokenError::NoFunds)?
} else {
(old_balance - amount, amount)
};
if new_balance < Self::minimum_balance() {
amount = amount.saturating_add(new_balance);
new_balance = Zero::zero();
}
// Defensive only - this should not fail now.
Self::set_balance(who, new_balance)?;
Ok(amount)
}
/// Reduce the balance of `who` by the most that is possible, up to `amount`.
///
/// Minimum balance will be respected and the returned imbalance may be up to
/// `Self::minimum_balance() - 1` greater than `amount`.
///
/// Return the imbalance by which the account was reduced.
fn decrease_balance_at_most(who: &AccountId, amount: Self::Balance)
-> Self::Balance
{
let old_balance = Self::balance(who);
let (mut new_balance, mut amount) = if old_balance < amount {
(Zero::zero(), old_balance)
} else {
(old_balance - amount, amount)
};
let minimum_balance = Self::minimum_balance();
if new_balance < minimum_balance {
amount = amount.saturating_add(new_balance);
new_balance = Zero::zero();
}
let mut r = Self::set_balance(who, new_balance);
if r.is_err() {
// Some error, probably because we tried to destroy an account which cannot be destroyed.
if new_balance.is_zero() && amount >= minimum_balance {
new_balance = minimum_balance;
amount -= minimum_balance;
r = Self::set_balance(who, new_balance);
}
if r.is_err() {
// Still an error. Apparently it's not possible to reduce at all.
amount = Zero::zero();
}
}
amount
}
/// Increase the balance of `who` by `amount`. If it cannot be increased by that amount
/// for some reason, return `Err` and don't increase it at all. If Ok, return the imbalance.
///
/// Minimum balance will be respected and an error will be returned if
/// `amount < Self::minimum_balance()` when the account of `who` is zero.
fn increase_balance(who: &AccountId, amount: Self::Balance)
-> Result<Self::Balance, DispatchError>
{
let old_balance = Self::balance(who);
let new_balance = old_balance.checked_add(&amount).ok_or(TokenError::Overflow)?;
if new_balance < Self::minimum_balance() {
Err(TokenError::BelowMinimum)?
}
if old_balance != new_balance {
Self::set_balance(who, new_balance)?;
}
Ok(amount)
}
/// Increase the balance of `who` by the most that is possible, up to `amount`.
///
/// Minimum balance will be respected and the returned imbalance will be zero in the case that
/// `amount < Self::minimum_balance()`.
///
/// Return the imbalance by which the account was increased.
fn increase_balance_at_most(who: &AccountId, amount: Self::Balance)
-> Self::Balance
{
let old_balance = Self::balance(who);
let mut new_balance = old_balance.saturating_add(amount);
let mut amount = new_balance - old_balance;
if new_balance < Self::minimum_balance() {
new_balance = Zero::zero();
amount = Zero::zero();
}
if old_balance == new_balance || Self::set_balance(who, new_balance).is_ok() {
amount
} else {
Zero::zero()
}
}
}
/// Simple handler for an imbalance drop which increases the total issuance of the system by the
/// imbalance amount. Used for leftover debt.
pub struct IncreaseIssuance<AccountId, U>(PhantomData<(AccountId, U)>);
impl<AccountId, U: Unbalanced<AccountId>> HandleImbalanceDrop<U::Balance>
for IncreaseIssuance<AccountId, U>
{
fn handle(amount: U::Balance) {
U::set_total_issuance(U::total_issuance().saturating_add(amount))
}
}
/// Simple handler for an imbalance drop which decreases the total issuance of the system by the
/// imbalance amount. Used for leftover credit.
pub struct DecreaseIssuance<AccountId, U>(PhantomData<(AccountId, U)>);
impl<AccountId, U: Unbalanced<AccountId>> HandleImbalanceDrop<U::Balance>
for DecreaseIssuance<AccountId, U>
{
fn handle(amount: U::Balance) {
U::set_total_issuance(U::total_issuance().saturating_sub(amount))
}
}
/// An imbalance type which uses `DecreaseIssuance` to deal with anything `Drop`ed.
///
/// Basically means that funds in someone's account have been removed and not yet placed anywhere
/// else. If it gets dropped, then those funds will be assumed to be "burned" and the total supply
/// will be accordingly decreased to ensure it equals the sum of the balances of all accounts.
type Credit<AccountId, U> = Imbalance<
<U as Inspect<AccountId>>::Balance,
DecreaseIssuance<AccountId, U>,
IncreaseIssuance<AccountId, U>,
>;
/// An imbalance type which uses `IncreaseIssuance` to deal with anything `Drop`ed.
///
/// Basically means that there are funds in someone's account whose origin is as yet unaccounted
/// for. If it gets dropped, then those funds will be assumed to be "minted" and the total supply
/// will be accordingly increased to ensure it equals the sum of the balances of all accounts.
type Debt<AccountId, U> = Imbalance<
<U as Inspect<AccountId>>::Balance,
IncreaseIssuance<AccountId, U>,
DecreaseIssuance<AccountId, U>,
>;
/// Create some `Credit` item. Only for internal use.
fn credit<AccountId, U: Unbalanced<AccountId>>(
amount: U::Balance,
) -> Credit<AccountId, U> {
Imbalance::new(amount)
}
/// Create some `Debt` item. Only for internal use.
fn debt<AccountId, U: Unbalanced<AccountId>>(
amount: U::Balance,
) -> Debt<AccountId, U> {
Imbalance::new(amount)
}
impl<AccountId, U: Unbalanced<AccountId>> Balanced<AccountId> for U {
type OnDropCredit = DecreaseIssuance<AccountId, U>;
type OnDropDebt = IncreaseIssuance<AccountId, U>;
fn rescind(amount: Self::Balance) -> Debt<AccountId, Self> {
let old = U::total_issuance();
let new = old.saturating_sub(amount);
U::set_total_issuance(new);
debt(old - new)
}
fn issue(amount: Self::Balance) -> Credit<AccountId, Self> {
let old = U::total_issuance();
let new = old.saturating_add(amount);
U::set_total_issuance(new);
credit(new - old)
}
fn slash(
who: &AccountId,
amount: Self::Balance,
) -> (Credit<AccountId, Self>, Self::Balance) {
let slashed = U::decrease_balance_at_most(who, amount);
// `slashed` could be less than, greater than or equal to `amount`.
// If slashed == amount, it means the account had at least amount in it and it could all be
// removed without a problem.
// If slashed > amount, it means the account had more than amount in it, but not enough more
// to push it over minimum_balance.
// If slashed < amount, it means the account didn't have enough in it to be reduced by
// `amount` without being destroyed.
(credit(slashed), amount.saturating_sub(slashed))
}
fn deposit(
who: &AccountId,
amount: Self::Balance
) -> Result<Debt<AccountId, Self>, DispatchError> {
let increase = U::increase_balance(who, amount)?;
Ok(debt(increase))
}
fn withdraw(
who: &AccountId,
amount: Self::Balance,
//TODO: liveness: ExistenceRequirement,
) -> Result<Credit<AccountId, Self>, DispatchError> {
let decrease = U::decrease_balance(who, amount)?;
Ok(credit(decrease))
}
}
@@ -0,0 +1,162 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The imbalance type and its associates, which handles keeps everything adding up properly with
//! unbalanced operations.
use super::*;
use sp_std::marker::PhantomData;
use sp_runtime::traits::Zero;
use super::misc::Balance;
use super::balanced::Balanced;
use crate::traits::misc::{TryDrop, SameOrOther};
use super::super::Imbalance as ImbalanceT;
/// Handler for when an imbalance gets dropped. This could handle either a credit (negative) or
/// debt (positive) imbalance.
pub trait HandleImbalanceDrop<Balance> {
/// Some something with the imbalance's value which is being dropped.
fn handle(amount: Balance);
}
/// An imbalance in the system, representing a divergence of recorded token supply from the sum of
/// the balances of all accounts. This is `must_use` in order to ensure it gets handled (placing
/// into an account, settling from an account or altering the supply).
///
/// Importantly, it has a special `Drop` impl, and cannot be created outside of this module.
#[must_use]
pub struct Imbalance<
B: Balance,
OnDrop: HandleImbalanceDrop<B>,
OppositeOnDrop: HandleImbalanceDrop<B>,
> {
amount: B,
_phantom: PhantomData<(OnDrop, OppositeOnDrop)>,
}
impl<
B: Balance,
OnDrop: HandleImbalanceDrop<B>,
OppositeOnDrop: HandleImbalanceDrop<B>
> Drop for Imbalance<B, OnDrop, OppositeOnDrop> {
fn drop(&mut self) {
if !self.amount.is_zero() {
OnDrop::handle(self.amount)
}
}
}
impl<
B: Balance,
OnDrop: HandleImbalanceDrop<B>,
OppositeOnDrop: HandleImbalanceDrop<B>,
> TryDrop for Imbalance<B, OnDrop, OppositeOnDrop> {
/// Drop an instance cleanly. Only works if its value represents "no-operation".
fn try_drop(self) -> Result<(), Self> {
self.drop_zero()
}
}
impl<
B: Balance,
OnDrop: HandleImbalanceDrop<B>,
OppositeOnDrop: HandleImbalanceDrop<B>,
> Default for Imbalance<B, OnDrop, OppositeOnDrop> {
fn default() -> Self {
Self::zero()
}
}
impl<
B: Balance,
OnDrop: HandleImbalanceDrop<B>,
OppositeOnDrop: HandleImbalanceDrop<B>,
> Imbalance<B, OnDrop, OppositeOnDrop> {
pub(crate) fn new(amount: B) -> Self {
Self { amount, _phantom: PhantomData }
}
}
impl<
B: Balance,
OnDrop: HandleImbalanceDrop<B>,
OppositeOnDrop: HandleImbalanceDrop<B>,
> ImbalanceT<B> for Imbalance<B, OnDrop, OppositeOnDrop> {
type Opposite = Imbalance<B, OppositeOnDrop, OnDrop>;
fn zero() -> Self {
Self { amount: Zero::zero(), _phantom: PhantomData }
}
fn drop_zero(self) -> Result<(), Self> {
if self.amount.is_zero() {
sp_std::mem::forget(self);
Ok(())
} else {
Err(self)
}
}
fn split(self, amount: B) -> (Self, Self) {
let first = self.amount.min(amount);
let second = self.amount - first;
sp_std::mem::forget(self);
(Imbalance::new(first), Imbalance::new(second))
}
fn merge(mut self, other: Self) -> Self {
self.amount = self.amount.saturating_add(other.amount);
sp_std::mem::forget(other);
self
}
fn subsume(&mut self, other: Self) {
self.amount = self.amount.saturating_add(other.amount);
sp_std::mem::forget(other);
}
fn offset(self, other: Imbalance<B, OppositeOnDrop, OnDrop>)
-> SameOrOther<Self, Imbalance<B, OppositeOnDrop, OnDrop>>
{
let (a, b) = (self.amount, other.amount);
sp_std::mem::forget((self, other));
if a == b {
SameOrOther::None
} else if a > b {
SameOrOther::Same(Imbalance::new(a - b))
} else {
SameOrOther::Other(Imbalance::<B, OppositeOnDrop, OnDrop>::new(b - a))
}
}
fn peek(&self) -> B {
self.amount
}
}
/// Imbalance implying that the total_issuance value is less than the sum of all account balances.
pub type DebtOf<AccountId, B> = Imbalance<
<B as Inspect<AccountId>>::Balance,
// This will generally be implemented by increasing the total_issuance value.
<B as Balanced<AccountId>>::OnDropDebt,
<B as Balanced<AccountId>>::OnDropCredit,
>;
/// Imbalance implying that the total_issuance value is greater than the sum of all account balances.
pub type CreditOf<AccountId, B> = Imbalance<
<B as Inspect<AccountId>>::Balance,
// This will generally be implemented by decreasing the total_issuance value.
<B as Balanced<AccountId>>::OnDropCredit,
<B as Balanced<AccountId>>::OnDropDebt,
>;
@@ -0,0 +1,143 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The traits for sets of fungible tokens and any associated types.
use super::*;
use crate::dispatch::{DispatchError, DispatchResult};
use super::misc::{AssetId, Balance};
use sp_runtime::traits::Saturating;
mod balanced;
pub use balanced::{Balanced, Unbalanced};
mod imbalance;
pub use imbalance::{Imbalance, HandleImbalanceDrop, DebtOf, CreditOf};
/// Trait for providing balance-inspection access to a set of named fungible assets.
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;
/// 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(
asset: Self::AssetId,
who: &AccountId,
amount: Self::Balance,
) -> WithdrawConsequence<Self::Balance>;
}
/// Trait for providing a set of named fungible assets which can be created and destroyed.
pub trait Mutate<AccountId>: Inspect<AccountId> {
/// Attempt to increase the `asset` balance of `who` by `amount`.
///
/// If not possible then don't do anything. Possible reasons for failure include:
/// - Minimum balance not met.
/// - Account cannot be created (e.g. because there is no provider reference and/or the asset
/// isn't considered worth anything).
///
/// 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;
/// Attempt to reduce the `asset` balance of `who` by `amount`.
///
/// If not possible then don't do anything. Possible reasons for failure include:
/// - Less funds in the account than `amount`
/// - Liquidity requirements (locks, reservations) prevent the funds from being removed
/// - Operation would require destroying the account and it is required to stay alive (e.g.
/// because it's providing a needed provider reference).
///
/// Since this is an operation which should be possible to take alone, if successful it will
/// reduce the overall supply of the underlying token.
///
/// 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)
-> Result<Self::Balance, DispatchError>;
/// Transfer funds from one account into another.
fn transfer(
asset: Self::AssetId,
source: &AccountId,
dest: &AccountId,
amount: Self::Balance,
) -> 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)?;
debug_assert!(actual == amount.saturating_add(extra), "can_withdraw must agree with withdraw; qed");
match Self::deposit(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);
debug_assert!(revert.is_ok(), "withdrew funds previously; qed");
Err(err)
}
}
}
}
/// Trait for providing a set of named fungible assets which can only be transferred.
pub trait Transfer<AccountId>: Inspect<AccountId> {
/// Transfer funds from one account into another.
fn transfer(
asset: Self::AssetId,
source: &AccountId,
dest: &AccountId,
amount: Self::Balance,
) -> 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;
/// 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;
}
@@ -0,0 +1,375 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The trait and associated types for sets of fungible tokens that manage total issuance without
//! requiring atomic balanced operations.
use super::*;
use sp_std::marker::PhantomData;
use sp_runtime::{TokenError, traits::{Zero, CheckedAdd}};
use sp_arithmetic::traits::Saturating;
use crate::dispatch::{DispatchError, DispatchResult};
use crate::traits::misc::{SameOrOther, TryDrop};
/// A fungible token class where any creation and deletion of tokens is semi-explicit and where the
/// total supply is maintained automatically.
///
/// This is auto-implemented when a token class has `Unbalanced` implemented.
pub trait Balanced<AccountId>: Inspect<AccountId> {
type OnDropDebt: HandleImbalanceDrop<Self::AssetId, Self::Balance>;
type OnDropCredit: HandleImbalanceDrop<Self::AssetId, Self::Balance>;
/// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will
/// typically be used to reduce an account by the same amount with e.g. `settle`.
///
/// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example
/// in the case of underflow.
fn rescind(asset: Self::AssetId, amount: Self::Balance) -> DebtOf<AccountId, Self>;
/// Increase the total issuance by `amount` and return the according imbalance. The imbalance
/// will typically be used to increase an account by the same amount with e.g.
/// `resolve_into_existing` or `resolve_creating`.
///
/// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example
/// in the case of overflow.
fn issue(asset: Self::AssetId, amount: Self::Balance) -> CreditOf<AccountId, Self>;
/// Produce a pair of imbalances that cancel each other out exactly.
///
/// This is just the same as burning and issuing the same amount and has no effect on the
/// total issuance.
fn pair(asset: Self::AssetId, amount: Self::Balance)
-> (DebtOf<AccountId, Self>, CreditOf<AccountId, Self>)
{
(Self::rescind(asset, amount), Self::issue(asset, amount))
}
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
/// free balance. This function cannot fail.
///
/// The resulting imbalance is the first item of the tuple returned.
///
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
/// then a non-zero second item will be returned.
fn slash(
asset: Self::AssetId,
who: &AccountId,
amount: Self::Balance,
) -> (CreditOf<AccountId, Self>, Self::Balance);
/// Mints exactly `value` into the `asset` account of `who`.
///
/// If `who` doesn't exist, nothing is done and an `Err` returned. This could happen because it
/// the account doesn't yet exist and it isn't possible to create it under the current
/// circumstances and with `value` in it.
fn deposit(
asset: Self::AssetId,
who: &AccountId,
value: Self::Balance,
) -> Result<DebtOf<AccountId, Self>, DispatchError>;
/// Removes `value` free `asset` balance from `who` account if possible.
///
/// If the removal is not possible, then it returns `Err` and nothing is changed.
///
/// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value
/// is no less than `value`. It may be more in the case that removing it reduced it below
/// `Self::minimum_balance()`.
fn withdraw(
asset: Self::AssetId,
who: &AccountId,
value: Self::Balance,
//TODO: liveness: ExistenceRequirement,
) -> Result<CreditOf<AccountId, Self>, DispatchError>;
/// The balance of `who` is increased in order to counter `credit`. If the whole of `credit`
/// cannot be countered, then nothing is changed and the original `credit` is returned in an
/// `Err`.
///
/// Please note: If `credit.peek()` is less than `Self::minimum_balance()`, then `who` must
/// already exist for this to succeed.
fn resolve(
who: &AccountId,
credit: CreditOf<AccountId, Self>,
) -> Result<(), CreditOf<AccountId, Self>> {
let v = credit.peek();
let debt = match Self::deposit(credit.asset(), who, v) {
Err(_) => return Err(credit),
Ok(d) => d,
};
if let Ok(result) = credit.offset(debt) {
let result = result.try_drop();
debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed");
} else {
debug_assert!(false, "debt.asset is credit.asset; qed");
}
Ok(())
}
/// The balance of `who` is decreased in order to counter `debt`. If the whole of `debt`
/// cannot be countered, then nothing is changed and the original `debt` is returned in an
/// `Err`.
fn settle(
who: &AccountId,
debt: DebtOf<AccountId, Self>,
//TODO: liveness: ExistenceRequirement,
) -> Result<CreditOf<AccountId, Self>, DebtOf<AccountId, Self>> {
let amount = debt.peek();
let asset = debt.asset();
let credit = match Self::withdraw(asset, who, amount) {
Err(_) => return Err(debt),
Ok(d) => d,
};
match credit.offset(debt) {
Ok(SameOrOther::None) => Ok(CreditOf::<AccountId, Self>::zero(asset)),
Ok(SameOrOther::Same(dust)) => Ok(dust),
Ok(SameOrOther::Other(rest)) => {
debug_assert!(false, "ok withdraw return must be at least debt value; qed");
Err(rest)
}
Err(_) => {
debug_assert!(false, "debt.asset is credit.asset; qed");
Ok(CreditOf::<AccountId, Self>::zero(asset))
}
}
}
}
/// A fungible token class where the balance can be set arbitrarily.
///
/// **WARNING**
/// Do not use this directly unless you want trouble, since it allows you to alter account balances
/// without keeping the issuance up to date. It has no safeguards against accidentally creating
/// token imbalances in your system leading to accidental imflation or deflation. It's really just
/// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to
/// use.
pub trait Unbalanced<AccountId>: Inspect<AccountId> {
/// Set the `asset` balance of `who` to `amount`. If this cannot be done for some reason (e.g.
/// because the account cannot be created or an overflow) then an `Err` is returned.
fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DispatchResult;
/// Set the total issuance of `asset` to `amount`.
fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance);
/// Reduce the `asset` balance of `who` by `amount`. If it cannot be reduced by that amount for
/// some reason, return `Err` and don't reduce it at all. If Ok, return the imbalance.
///
/// Minimum balance will be respected and the returned imbalance may be up to
/// `Self::minimum_balance() - 1` greater than `amount`.
fn decrease_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
-> Result<Self::Balance, DispatchError>
{
let old_balance = Self::balance(asset, who);
let (mut new_balance, mut amount) = if old_balance < amount {
Err(TokenError::NoFunds)?
} else {
(old_balance - amount, amount)
};
if new_balance < Self::minimum_balance(asset) {
amount = amount.saturating_add(new_balance);
new_balance = Zero::zero();
}
// Defensive only - this should not fail now.
Self::set_balance(asset, who, new_balance)?;
Ok(amount)
}
/// Reduce the `asset` balance of `who` by the most that is possible, up to `amount`.
///
/// Minimum balance will be respected and the returned imbalance may be up to
/// `Self::minimum_balance() - 1` greater than `amount`.
///
/// Return the imbalance by which the account was reduced.
fn decrease_balance_at_most(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
-> Self::Balance
{
let old_balance = Self::balance(asset, who);
let (mut new_balance, mut amount) = if old_balance < amount {
(Zero::zero(), old_balance)
} else {
(old_balance - amount, amount)
};
let minimum_balance = Self::minimum_balance(asset);
if new_balance < minimum_balance {
amount = amount.saturating_add(new_balance);
new_balance = Zero::zero();
}
let mut r = Self::set_balance(asset, who, new_balance);
if r.is_err() {
// Some error, probably because we tried to destroy an account which cannot be destroyed.
if new_balance.is_zero() && amount >= minimum_balance {
new_balance = minimum_balance;
amount -= minimum_balance;
r = Self::set_balance(asset, who, new_balance);
}
if r.is_err() {
// Still an error. Apparently it's not possible to reduce at all.
amount = Zero::zero();
}
}
amount
}
/// Increase the `asset` balance of `who` by `amount`. If it cannot be increased by that amount
/// for some reason, return `Err` and don't increase it at all. If Ok, return the imbalance.
///
/// Minimum balance will be respected and an error will be returned if
/// `amount < Self::minimum_balance()` when the account of `who` is zero.
fn increase_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
-> Result<Self::Balance, DispatchError>
{
let old_balance = Self::balance(asset, who);
let new_balance = old_balance.checked_add(&amount).ok_or(TokenError::Overflow)?;
if new_balance < Self::minimum_balance(asset) {
Err(TokenError::BelowMinimum)?
}
if old_balance != new_balance {
Self::set_balance(asset, who, new_balance)?;
}
Ok(amount)
}
/// Increase the `asset` balance of `who` by the most that is possible, up to `amount`.
///
/// Minimum balance will be respected and the returned imbalance will be zero in the case that
/// `amount < Self::minimum_balance()`.
///
/// Return the imbalance by which the account was increased.
fn increase_balance_at_most(asset: Self::AssetId, who: &AccountId, amount: Self::Balance)
-> Self::Balance
{
let old_balance = Self::balance(asset, who);
let mut new_balance = old_balance.saturating_add(amount);
let mut amount = new_balance - old_balance;
if new_balance < Self::minimum_balance(asset) {
new_balance = Zero::zero();
amount = Zero::zero();
}
if old_balance == new_balance || Self::set_balance(asset, who, new_balance).is_ok() {
amount
} else {
Zero::zero()
}
}
}
/// Simple handler for an imbalance drop which increases the total issuance of the system by the
/// imbalance amount. Used for leftover debt.
pub struct IncreaseIssuance<AccountId, U>(PhantomData<(AccountId, U)>);
impl<AccountId, U: Unbalanced<AccountId>> HandleImbalanceDrop<U::AssetId, U::Balance>
for IncreaseIssuance<AccountId, U>
{
fn handle(asset: U::AssetId, amount: U::Balance) {
U::set_total_issuance(asset, U::total_issuance(asset).saturating_add(amount))
}
}
/// Simple handler for an imbalance drop which decreases the total issuance of the system by the
/// imbalance amount. Used for leftover credit.
pub struct DecreaseIssuance<AccountId, U>(PhantomData<(AccountId, U)>);
impl<AccountId, U: Unbalanced<AccountId>> HandleImbalanceDrop<U::AssetId, U::Balance>
for DecreaseIssuance<AccountId, U>
{
fn handle(asset: U::AssetId, amount: U::Balance) {
U::set_total_issuance(asset, U::total_issuance(asset).saturating_sub(amount))
}
}
/// An imbalance type which uses `DecreaseIssuance` to deal with anything `Drop`ed.
///
/// Basically means that funds in someone's account have been removed and not yet placed anywhere
/// else. If it gets dropped, then those funds will be assumed to be "burned" and the total supply
/// will be accordingly decreased to ensure it equals the sum of the balances of all accounts.
type Credit<AccountId, U> = Imbalance<
<U as Inspect<AccountId>>::AssetId,
<U as Inspect<AccountId>>::Balance,
DecreaseIssuance<AccountId, U>,
IncreaseIssuance<AccountId, U>,
>;
/// An imbalance type which uses `IncreaseIssuance` to deal with anything `Drop`ed.
///
/// Basically means that there are funds in someone's account whose origin is as yet unaccounted
/// for. If it gets dropped, then those funds will be assumed to be "minted" and the total supply
/// will be accordingly increased to ensure it equals the sum of the balances of all accounts.
type Debt<AccountId, U> = Imbalance<
<U as Inspect<AccountId>>::AssetId,
<U as Inspect<AccountId>>::Balance,
IncreaseIssuance<AccountId, U>,
DecreaseIssuance<AccountId, U>,
>;
/// Create some `Credit` item. Only for internal use.
fn credit<AccountId, U: Unbalanced<AccountId>>(
asset: U::AssetId,
amount: U::Balance,
) -> Credit<AccountId, U> {
Imbalance::new(asset, amount)
}
/// Create some `Debt` item. Only for internal use.
fn debt<AccountId, U: Unbalanced<AccountId>>(
asset: U::AssetId,
amount: U::Balance,
) -> Debt<AccountId, U> {
Imbalance::new(asset, amount)
}
impl<AccountId, U: Unbalanced<AccountId>> Balanced<AccountId> for U {
type OnDropCredit = DecreaseIssuance<AccountId, U>;
type OnDropDebt = IncreaseIssuance<AccountId, U>;
fn rescind(asset: Self::AssetId, amount: Self::Balance) -> Debt<AccountId, Self> {
U::set_total_issuance(asset, U::total_issuance(asset).saturating_sub(amount));
debt(asset, amount)
}
fn issue(asset: Self::AssetId, amount: Self::Balance) -> Credit<AccountId, Self> {
U::set_total_issuance(asset, U::total_issuance(asset).saturating_add(amount));
credit(asset, amount)
}
fn slash(
asset: Self::AssetId,
who: &AccountId,
amount: Self::Balance,
) -> (Credit<AccountId, Self>, Self::Balance) {
let slashed = U::decrease_balance_at_most(asset, who, amount);
// `slashed` could be less than, greater than or equal to `amount`.
// If slashed == amount, it means the account had at least amount in it and it could all be
// removed without a problem.
// If slashed > amount, it means the account had more than amount in it, but not enough more
// to push it over minimum_balance.
// If slashed < amount, it means the account didn't have enough in it to be reduced by
// `amount` without being destroyed.
(credit(asset, slashed), amount.saturating_sub(slashed))
}
fn deposit(
asset: Self::AssetId,
who: &AccountId,
amount: Self::Balance
) -> Result<Debt<AccountId, Self>, DispatchError> {
let increase = U::increase_balance(asset, who, amount)?;
Ok(debt(asset, increase))
}
fn withdraw(
asset: Self::AssetId,
who: &AccountId,
amount: Self::Balance,
//TODO: liveness: ExistenceRequirement,
) -> Result<Credit<AccountId, Self>, DispatchError> {
let decrease = U::decrease_balance(asset, who, amount)?;
Ok(credit(asset, decrease))
}
}
@@ -0,0 +1,169 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The imbalance type and its associates, which handles keeps everything adding up properly with
//! unbalanced operations.
use super::*;
use sp_std::marker::PhantomData;
use sp_runtime::traits::Zero;
use super::fungibles::{AssetId, Balance};
use super::balanced::Balanced;
use crate::traits::misc::{TryDrop, SameOrOther};
/// Handler for when an imbalance gets dropped. This could handle either a credit (negative) or
/// debt (positive) imbalance.
pub trait HandleImbalanceDrop<AssetId, Balance> {
fn handle(asset: AssetId, amount: Balance);
}
/// An imbalance in the system, representing a divergence of recorded token supply from the sum of
/// the balances of all accounts. This is `must_use` in order to ensure it gets handled (placing
/// into an account, settling from an account or altering the supply).
///
/// Importantly, it has a special `Drop` impl, and cannot be created outside of this module.
#[must_use]
pub struct Imbalance<
A: AssetId,
B: Balance,
OnDrop: HandleImbalanceDrop<A, B>,
OppositeOnDrop: HandleImbalanceDrop<A, B>,
> {
asset: A,
amount: B,
_phantom: PhantomData<(OnDrop, OppositeOnDrop)>,
}
impl<
A: AssetId,
B: Balance,
OnDrop: HandleImbalanceDrop<A, B>,
OppositeOnDrop: HandleImbalanceDrop<A, B>
> Drop for Imbalance<A, B, OnDrop, OppositeOnDrop> {
fn drop(&mut self) {
if !self.amount.is_zero() {
OnDrop::handle(self.asset, self.amount)
}
}
}
impl<
A: AssetId,
B: Balance,
OnDrop: HandleImbalanceDrop<A, B>,
OppositeOnDrop: HandleImbalanceDrop<A, B>,
> TryDrop for Imbalance<A, B, OnDrop, OppositeOnDrop> {
/// Drop an instance cleanly. Only works if its value represents "no-operation".
fn try_drop(self) -> Result<(), Self> {
self.drop_zero()
}
}
impl<
A: AssetId,
B: Balance,
OnDrop: HandleImbalanceDrop<A, B>,
OppositeOnDrop: HandleImbalanceDrop<A, B>,
> Imbalance<A, B, OnDrop, OppositeOnDrop> {
pub fn zero(asset: A) -> Self {
Self { asset, amount: Zero::zero(), _phantom: PhantomData }
}
pub(crate) fn new(asset: A, amount: B) -> Self {
Self { asset, amount, _phantom: PhantomData }
}
pub fn drop_zero(self) -> Result<(), Self> {
if self.amount.is_zero() {
sp_std::mem::forget(self);
Ok(())
} else {
Err(self)
}
}
pub fn split(self, amount: B) -> (Self, Self) {
let first = self.amount.min(amount);
let second = self.amount - first;
let asset = self.asset;
sp_std::mem::forget(self);
(Imbalance::new(asset, first), Imbalance::new(asset, second))
}
pub fn merge(mut self, other: Self) -> Result<Self, (Self, Self)> {
if self.asset == other.asset {
self.amount = self.amount.saturating_add(other.amount);
sp_std::mem::forget(other);
Ok(self)
} else {
Err((self, other))
}
}
pub fn subsume(&mut self, other: Self) -> Result<(), Self> {
if self.asset == other.asset {
self.amount = self.amount.saturating_add(other.amount);
sp_std::mem::forget(other);
Ok(())
} else {
Err(other)
}
}
pub fn offset(self, other: Imbalance<A, B, OppositeOnDrop, OnDrop>) -> Result<
SameOrOther<Self, Imbalance<A, B, OppositeOnDrop, OnDrop>>,
(Self, Imbalance<A, B, OppositeOnDrop, OnDrop>),
> {
if self.asset == other.asset {
let (a, b) = (self.amount, other.amount);
let asset = self.asset;
sp_std::mem::forget((self, other));
if a == b {
Ok(SameOrOther::None)
} else if a > b {
Ok(SameOrOther::Same(Imbalance::new(asset, a - b)))
} else {
Ok(SameOrOther::Other(Imbalance::<A, B, OppositeOnDrop, OnDrop>::new(asset, b - a)))
}
} else {
Err((self, other))
}
}
pub fn peek(&self) -> B {
self.amount
}
pub fn asset(&self) -> A {
self.asset
}
}
/// Imbalance implying that the total_issuance value is less than the sum of all account balances.
pub type DebtOf<AccountId, B> = Imbalance<
<B as Inspect<AccountId>>::AssetId,
<B as Inspect<AccountId>>::Balance,
// This will generally be implemented by increasing the total_issuance value.
<B as Balanced<AccountId>>::OnDropDebt,
<B as Balanced<AccountId>>::OnDropCredit,
>;
/// Imbalance implying that the total_issuance value is greater than the sum of all account balances.
pub type CreditOf<AccountId, B> = Imbalance<
<B as Inspect<AccountId>>::AssetId,
<B as Inspect<AccountId>>::Balance,
// This will generally be implemented by decreasing the total_issuance value.
<B as Balanced<AccountId>>::OnDropCredit,
<B as Balanced<AccountId>>::OnDropDebt,
>;
@@ -0,0 +1,174 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The imbalance trait type and its associates, which handles keeps everything adding up properly
//! with unbalanced operations.
use sp_std::ops::Div;
use sp_runtime::traits::Saturating;
use crate::traits::misc::{TryDrop, SameOrOther};
mod split_two_ways;
mod signed_imbalance;
mod on_unbalanced;
pub use split_two_ways::SplitTwoWays;
pub use signed_imbalance::SignedImbalance;
pub use on_unbalanced::OnUnbalanced;
/// A trait for a not-quite Linear Type that tracks an imbalance.
///
/// Functions that alter account balances return an object of this trait to
/// express how much account balances have been altered in aggregate. If
/// dropped, the currency system will take some default steps to deal with
/// the imbalance (`balances` module simply reduces or increases its
/// total issuance). Your module should generally handle it in some way,
/// good practice is to do so in a configurable manner using an
/// `OnUnbalanced` type for each situation in which your module needs to
/// handle an imbalance.
///
/// Imbalances can either be Positive (funds were added somewhere without
/// being subtracted elsewhere - e.g. a reward) or Negative (funds deducted
/// somewhere without an equal and opposite addition - e.g. a slash or
/// system fee payment).
///
/// Since they are unsigned, the actual type is always Positive or Negative.
/// The trait makes no distinction except to define the `Opposite` type.
///
/// New instances of zero value can be created (`zero`) and destroyed
/// (`drop_zero`).
///
/// Existing instances can be `split` and merged either consuming `self` with
/// `merge` or mutating `self` with `subsume`. If the target is an `Option`,
/// then `maybe_merge` and `maybe_subsume` might work better. Instances can
/// also be `offset` with an `Opposite` that is less than or equal to in value.
///
/// You can always retrieve the raw balance value using `peek`.
#[must_use]
pub trait Imbalance<Balance>: Sized + TryDrop + Default {
/// The oppositely imbalanced type. They come in pairs.
type Opposite: Imbalance<Balance>;
/// The zero imbalance. Can be destroyed with `drop_zero`.
fn zero() -> Self;
/// Drop an instance cleanly. Only works if its `self.value()` is zero.
fn drop_zero(self) -> Result<(), Self>;
/// Consume `self` and return two independent instances; the first
/// is guaranteed to be at most `amount` and the second will be the remainder.
fn split(self, amount: Balance) -> (Self, Self);
/// Consume `self` and return two independent instances; the amounts returned will be in
/// approximately the same ratio as `first`:`second`.
///
/// NOTE: This requires up to `first + second` room for a multiply, and `first + second` should
/// fit into a `u32`. Overflow will safely saturate in both cases.
fn ration(self, first: u32, second: u32) -> (Self, Self)
where Balance: From<u32> + Saturating + Div<Output=Balance>
{
let total: u32 = first.saturating_add(second);
if total == 0 { return (Self::zero(), Self::zero()) }
let amount1 = self.peek().saturating_mul(first.into()) / total.into();
self.split(amount1)
}
/// Consume self and add its two components, defined by the first component's balance,
/// element-wise to two pre-existing Imbalances.
///
/// A convenient replacement for `split` and `merge`.
fn split_merge(self, amount: Balance, others: (Self, Self)) -> (Self, Self) {
let (a, b) = self.split(amount);
(a.merge(others.0), b.merge(others.1))
}
/// Consume self and add its two components, defined by the ratio `first`:`second`,
/// element-wise to two pre-existing Imbalances.
///
/// A convenient replacement for `split` and `merge`.
fn ration_merge(self, first: u32, second: u32, others: (Self, Self)) -> (Self, Self)
where Balance: From<u32> + Saturating + Div<Output=Balance>
{
let (a, b) = self.ration(first, second);
(a.merge(others.0), b.merge(others.1))
}
/// Consume self and add its two components, defined by the first component's balance,
/// element-wise into two pre-existing Imbalance refs.
///
/// A convenient replacement for `split` and `subsume`.
fn split_merge_into(self, amount: Balance, others: &mut (Self, Self)) {
let (a, b) = self.split(amount);
others.0.subsume(a);
others.1.subsume(b);
}
/// Consume self and add its two components, defined by the ratio `first`:`second`,
/// element-wise to two pre-existing Imbalances.
///
/// A convenient replacement for `split` and `merge`.
fn ration_merge_into(self, first: u32, second: u32, others: &mut (Self, Self))
where Balance: From<u32> + Saturating + Div<Output=Balance>
{
let (a, b) = self.ration(first, second);
others.0.subsume(a);
others.1.subsume(b);
}
/// Consume `self` and an `other` to return a new instance that combines
/// both.
fn merge(self, other: Self) -> Self;
/// Consume self to mutate `other` so that it combines both. Just like `subsume`, only with
/// reversed arguments.
fn merge_into(self, other: &mut Self) {
other.subsume(self)
}
/// Consume `self` and maybe an `other` to return a new instance that combines
/// both.
fn maybe_merge(self, other: Option<Self>) -> Self {
if let Some(o) = other {
self.merge(o)
} else {
self
}
}
/// Consume an `other` to mutate `self` into a new instance that combines
/// both.
fn subsume(&mut self, other: Self);
/// Maybe consume an `other` to mutate `self` into a new instance that combines
/// both.
fn maybe_subsume(&mut self, other: Option<Self>) {
if let Some(o) = other {
self.subsume(o)
}
}
/// Consume self and along with an opposite counterpart to return
/// a combined result.
///
/// Returns `Ok` along with a new instance of `Self` if this instance has a
/// greater value than the `other`. Otherwise returns `Err` with an instance of
/// the `Opposite`. In both cases the value represents the combination of `self`
/// and `other`.
fn offset(self, other: Self::Opposite)-> SameOrOther<Self, Self::Opposite>;
/// The raw value of self.
fn peek(&self) -> Balance;
}
@@ -0,0 +1,50 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Trait for handling imbalances.
use crate::traits::misc::TryDrop;
/// Handler for when some currency "account" decreased in balance for
/// some reason.
///
/// The only reason at present for an increase would be for validator rewards, but
/// there may be other reasons in the future or for other chains.
///
/// Reasons for decreases include:
///
/// - Someone got slashed.
/// - Someone paid for a transaction to be included.
pub trait OnUnbalanced<Imbalance: TryDrop> {
/// Handler for some imbalances. The different imbalances might have different origins or
/// meanings, dependent on the context. Will default to simply calling on_unbalanced for all
/// of them. Infallible.
fn on_unbalanceds<B>(amounts: impl Iterator<Item=Imbalance>) where Imbalance: crate::traits::Imbalance<B> {
Self::on_unbalanced(amounts.fold(Imbalance::zero(), |i, x| x.merge(i)))
}
/// Handler for some imbalance. Infallible.
fn on_unbalanced(amount: Imbalance) {
amount.try_drop().unwrap_or_else(Self::on_nonzero_unbalanced)
}
/// Actually handle a non-zero imbalance. You probably want to implement this rather than
/// `on_unbalanced`.
fn on_nonzero_unbalanced(amount: Imbalance) { drop(amount); }
}
impl<Imbalance: TryDrop> OnUnbalanced<Imbalance> for () {}
@@ -0,0 +1,69 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Convenience type for managing an imbalance whose sign is unknown.
use codec::FullCodec;
use sp_std::fmt::Debug;
use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize};
use crate::traits::misc::SameOrOther;
use super::super::imbalance::Imbalance;
/// Either a positive or a negative imbalance.
pub enum SignedImbalance<B, PositiveImbalance: Imbalance<B>>{
/// A positive imbalance (funds have been created but none destroyed).
Positive(PositiveImbalance),
/// A negative imbalance (funds have been destroyed but none created).
Negative(PositiveImbalance::Opposite),
}
impl<
P: Imbalance<B, Opposite=N>,
N: Imbalance<B, Opposite=P>,
B: AtLeast32BitUnsigned + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default,
> SignedImbalance<B, P> {
/// Create a `Positive` instance of `Self` whose value is zero.
pub fn zero() -> Self {
SignedImbalance::Positive(P::zero())
}
/// Drop `Self` if and only if it is equal to zero. Return `Err` with `Self` if not.
pub fn drop_zero(self) -> Result<(), Self> {
match self {
SignedImbalance::Positive(x) => x.drop_zero().map_err(SignedImbalance::Positive),
SignedImbalance::Negative(x) => x.drop_zero().map_err(SignedImbalance::Negative),
}
}
/// Consume `self` and an `other` to return a new instance that combines
/// both.
pub fn merge(self, other: Self) -> Self {
match (self, other) {
(SignedImbalance::Positive(one), SignedImbalance::Positive(other)) =>
SignedImbalance::Positive(one.merge(other)),
(SignedImbalance::Negative(one), SignedImbalance::Negative(other)) =>
SignedImbalance::Negative(one.merge(other)),
(SignedImbalance::Positive(one), SignedImbalance::Negative(other)) =>
match one.offset(other) {
SameOrOther::Same(positive) => SignedImbalance::Positive(positive),
SameOrOther::Other(negative) => SignedImbalance::Negative(negative),
SameOrOther::None => SignedImbalance::Positive(P::zero()),
},
(one, other) => other.merge(one),
}
}
}
@@ -0,0 +1,51 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Means for splitting an imbalance into two and hanlding them differently.
use sp_std::{ops::Div, marker::PhantomData};
use sp_core::u32_trait::Value as U32;
use sp_runtime::traits::Saturating;
use super::super::imbalance::{Imbalance, OnUnbalanced};
/// Split an unbalanced amount two ways between a common divisor.
pub struct SplitTwoWays<
Balance,
Imbalance,
Part1,
Target1,
Part2,
Target2,
>(PhantomData<(Balance, Imbalance, Part1, Target1, Part2, Target2)>);
impl<
Balance: From<u32> + Saturating + Div<Output=Balance>,
I: Imbalance<Balance>,
Part1: U32,
Target1: OnUnbalanced<I>,
Part2: U32,
Target2: OnUnbalanced<I>,
> OnUnbalanced<I> for SplitTwoWays<Balance, I, Part1, Target1, Part2, Target2>
{
fn on_nonzero_unbalanced(amount: I) {
let total: u32 = Part1::VALUE + Part2::VALUE;
let amount1 = amount.peek().saturating_mul(Part1::VALUE.into()) / total.into();
let (imb1, imb2) = amount.split(amount1);
Target1::on_unbalanced(imb1);
Target2::on_unbalanced(imb2);
}
}
@@ -0,0 +1,164 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Miscellaneous types.
use codec::{Encode, Decode, FullCodec};
use sp_core::RuntimeDebug;
use sp_arithmetic::traits::{Zero, AtLeast32BitUnsigned};
use sp_runtime::TokenError;
/// One of a number of consequences of withdrawing a fungible from an account.
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum WithdrawConsequence<Balance> {
/// Withdraw could not happen since the amount to be withdrawn is less than the total funds in
/// the account.
NoFunds,
/// The withdraw would mean the account dying when it needs to exist (usually because it is a
/// provider and there are consumer references on it).
WouldDie,
/// The asset is unknown. Usually because an `AssetId` has been presented which doesn't exist
/// on the system.
UnknownAsset,
/// There has been an underflow in the system. This is indicative of a corrupt state and
/// likely unrecoverable.
Underflow,
/// 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
/// amount of balance which is destroyed.
ReducedToZero(Balance),
/// Account continued in existence.
Success,
}
impl<Balance: Zero> WithdrawConsequence<Balance> {
/// Convert the type into a `Result` with `TokenError` as the error or the additional `Balance`
/// by which the account will be reduced.
pub fn into_result(self) -> Result<Balance, TokenError> {
use WithdrawConsequence::*;
match self {
NoFunds => Err(TokenError::NoFunds),
WouldDie => Err(TokenError::WouldDie),
UnknownAsset => Err(TokenError::UnknownAsset),
Underflow => Err(TokenError::Underflow),
Frozen => Err(TokenError::Frozen),
ReducedToZero(result) => Ok(result),
Success => Ok(Zero::zero()),
}
}
}
/// One of a number of consequences of withdrawing a fungible from an account.
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum DepositConsequence {
/// Deposit couldn't happen due to the amount being too low. This is usually because the
/// account doesn't yet exist and the deposit wouldn't bring it to at least the minimum needed
/// for existance.
BelowMinimum,
/// Deposit cannot happen since the account cannot be created (usually because it's a consumer
/// and there exists no provider reference).
CannotCreate,
/// The asset is unknown. Usually because an `AssetId` has been presented which doesn't exist
/// on the system.
UnknownAsset,
/// An overflow would occur. This is practically unexpected, but could happen in test systems
/// with extremely small balance types or balances that approach the max value of the balance
/// type.
Overflow,
/// Account continued in existence.
Success,
}
impl DepositConsequence {
/// Convert the type into a `Result` with `TokenError` as the error.
pub fn into_result(self) -> Result<(), TokenError> {
use DepositConsequence::*;
Err(match self {
BelowMinimum => TokenError::BelowMinimum,
CannotCreate => TokenError::CannotCreate,
UnknownAsset => TokenError::UnknownAsset,
Overflow => TokenError::Overflow,
Success => return Ok(()),
})
}
}
/// Simple boolean for whether an account needs to be kept in existence.
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum ExistenceRequirement {
/// Operation must not result in the account going out of existence.
///
/// Note this implies that if the account never existed in the first place, then the operation
/// may legitimately leave the account unchanged and still non-existent.
KeepAlive,
/// Operation may result in account going out of existence.
AllowDeath,
}
/// Status of funds.
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)]
pub enum BalanceStatus {
/// Funds are free, as corresponding to `free` item in Balances.
Free,
/// Funds are reserved, as corresponding to `reserved` item in Balances.
Reserved,
}
bitflags::bitflags! {
/// Reasons for moving funds out of an account.
#[derive(Encode, Decode)]
pub struct WithdrawReasons: i8 {
/// In order to pay for (system) transaction costs.
const TRANSACTION_PAYMENT = 0b00000001;
/// In order to transfer ownership.
const TRANSFER = 0b00000010;
/// In order to reserve some funds for a later return or repatriation.
const RESERVE = 0b00000100;
/// In order to pay some other (higher-level) fees.
const FEE = 0b00001000;
/// In order to tip a validator for transaction inclusion.
const TIP = 0b00010000;
}
}
impl WithdrawReasons {
/// Choose all variants except for `one`.
///
/// ```rust
/// # use frame_support::traits::WithdrawReasons;
/// # fn main() {
/// assert_eq!(
/// WithdrawReasons::FEE | WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE | WithdrawReasons::TIP,
/// WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT),
/// );
/// # }
/// ```
pub fn except(one: WithdrawReasons) -> WithdrawReasons {
let mut flags = Self::all();
flags.toggle(one);
flags
}
}
/// Simple amalgamation trait to collect together properties for an AssetId under one roof.
pub trait AssetId: FullCodec + Copy + Default + Eq + PartialEq {}
impl<T: FullCodec + Copy + Default + Eq + PartialEq> AssetId for T {}
/// Simple amalgamation trait to collect together properties for a Balance under one roof.
pub trait Balance: AtLeast32BitUnsigned + FullCodec + Copy + Default {}
impl<T: AtLeast32BitUnsigned + FullCodec + Copy + Default> Balance for T {}