1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
// This file is part of Substrate.
// Copyright (C) 2019-2022 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::{super::Imbalance as ImbalanceT, *};
use crate::{
dispatch::{DispatchError, DispatchResult},
traits::misc::{SameOrOther, TryDrop},
};
use sp_runtime::{
traits::{CheckedAdd, Zero},
ArithmeticError, TokenError,
};
use sp_std::marker::PhantomData;
/// 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`. 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 {
return Err(TokenError::NoFunds.into())
} 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(ArithmeticError::Overflow)?;
if new_balance < Self::minimum_balance() {
return Err(TokenError::BelowMinimum.into())
}
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))
}
}