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
// This file is part of Substrate.
// Copyright (C) 2017-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.
//! Various basic types for use in the assets pallet.
use super::*;
use frame_support::sp_io::hashing::blake2_256;
use frame_support::{
pallet_prelude::*,
traits::{fungible, tokens::BalanceConversion},
};
use sp_runtime::{traits::Convert, FixedPointNumber, FixedPointOperand, FixedU128};
pub(super) type DepositBalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
pub(super) type AssetAccountOf<T, I> =
AssetAccount<<T as Config<I>>::Balance, DepositBalanceOf<T, I>, <T as Config<I>>::Extra>;
/// AssetStatus holds the current state of the asset. It could either be Live and available for use,
/// or in a Destroying state.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub(super) enum AssetStatus {
/// The asset is active and able to be used.
Live,
/// Whether the asset is frozen for non-admin transfers.
Frozen,
/// The asset is currently being destroyed, and all actions are no longer permitted on the
/// asset. Once set to `Destroying`, the asset can never transition back to a `Live` state.
Destroying,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetDetails<Balance: AtLeast32BitUnsigned + Copy, AccountId, DepositBalance> {
/// Can change `owner`, `issuer`, `freezer` and `admin` accounts.
pub(super) owner: AccountId,
/// Can mint tokens.
pub(super) issuer: AccountId,
/// Can thaw tokens, force transfers and burn tokens from any account.
pub(super) admin: AccountId,
/// Can freeze tokens.
pub(super) freezer: AccountId,
/// The total supply across all accounts.
pub(super) supply: Balance,
/// The total reserved balance across all accounts
pub(super) reserved: Balance,
/// The balance deposited for this asset. This pays for the data stored here.
pub(super) deposit: DepositBalance,
/// The ED for virtual accounts.
pub(super) min_balance: Balance,
/// If `true`, then any account with this asset is given a provider reference. Otherwise, it
/// requires a consumer reference.
pub(super) is_sufficient: bool,
/// The total number of accounts.
pub(super) accounts: u32,
/// The total number of accounts for which we have placed a self-sufficient reference.
pub(super) sufficients: u32,
/// The total number of approvals.
pub(super) approvals: u32,
/// Whether the asset is frozen for non-admin transfers.
pub(super) is_frozen: bool,
/// The status of the asset
pub(super) status: AssetStatus,
}
impl<Balance: AtLeast32BitUnsigned + Copy, AccountId, DepositBalance>
AssetDetails<Balance, AccountId, DepositBalance>
{
/* pub fn destroy_witness(&self) -> DestroyWitness {
DestroyWitness {
accounts: self.accounts,
sufficients: self.sufficients,
approvals: self.approvals,
}
} */
pub fn free_supply(&self) -> Balance {
self.supply.saturating_sub(self.reserved)
}
}
/// Data concerning an approval.
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, MaxEncodedLen, TypeInfo)]
pub struct Approval<Balance, DepositBalance> {
/// The amount of funds approved for the balance transfer from the owner to some delegated
/// target.
pub(super) amount: Balance,
/// The amount reserved on the owner's account to hold this item in storage.
pub(super) deposit: DepositBalance,
}
#[test]
fn ensure_bool_decodes_to_consumer_or_sufficient() {
assert_eq!(false.encode(), ExistenceReason::<()>::Consumer.encode());
assert_eq!(true.encode(), ExistenceReason::<()>::Sufficient.encode());
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub enum ExistenceReason<Balance> {
#[codec(index = 0)]
Consumer,
#[codec(index = 1)]
Sufficient,
#[codec(index = 2)]
DepositHeld(Balance),
#[codec(index = 3)]
DepositRefunded,
}
impl<Balance> ExistenceReason<Balance> {
pub(crate) fn take_deposit(&mut self) -> Option<Balance> {
if !matches!(self, ExistenceReason::DepositHeld(_)) {
return None;
}
if let ExistenceReason::DepositHeld(deposit) =
sp_std::mem::replace(self, ExistenceReason::DepositRefunded)
{
Some(deposit)
} else {
None
}
}
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetAccount<Balance, DepositBalance, Extra> {
/// The free balance.
pub(super) balance: Balance,
/// The reserved balance.
pub(super) reserved: Balance,
/// Whether the account is frozen.
pub(super) is_frozen: bool,
/// The reason for the existence of the account.
pub(super) reason: ExistenceReason<DepositBalance>,
/// Additional "sidecar" data, in case some other pallet wants to use this storage item.
pub(super) extra: Extra,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetMetadata<DepositBalance, BoundedString> {
/// The balance deposited for this metadata.
///
/// This pays for the data stored in this struct.
pub(super) deposit: DepositBalance,
/// The user friendly name of this asset. Limited in length by `StringLimit`.
pub(super) name: BoundedString,
/// The ticker symbol for this asset. Limited in length by `StringLimit`.
pub(super) symbol: BoundedString,
/// The number of decimals this asset uses to represent one unit.
pub(super) decimals: u8,
/// Whether the asset metadata may be changed by a non Force origin.
pub(super) is_frozen: bool,
}
/* /// Witness data for the destroy transactions.
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct DestroyWitness {
/// The number of accounts holding the asset.
#[codec(compact)]
pub(super) accounts: u32,
/// The number of accounts holding the asset with a self-sufficient reference.
#[codec(compact)]
pub(super) sufficients: u32,
/// The number of transfer-approvals of the asset.
#[codec(compact)]
pub(super) approvals: u32,
} */
/// Trait for allowing a minimum balance on the account to be specified, beyond the
/// `minimum_balance` of the asset. This is additive - the `minimum_balance` of the asset must be
/// met *and then* anything here in addition.
pub trait FrozenBalance<AssetId, AccountId, Balance> {
/// Return the frozen balance.
///
/// Generally, the balance of every account must be at least the sum of this (if `Some`) and
/// the asset's `minimum_balance` (the latter since there may be complications to destroying an
/// asset's account completely).
///
/// Under normal behaviour, the account balance should not go below the sum of this (if `Some`)
/// and the asset's minimum balance. However, the account balance may reasonably begin below
/// this sum (e.g. if less than the sum had ever been transferred into the account).
///
/// In special cases (privileged intervention) the account balance may also go below the sum.
///
/// If `None` is returned, then nothing special is enforced.
fn frozen_balance(asset: AssetId, who: &AccountId) -> Option<Balance>;
/// Called after an account has been removed.
///
/// NOTE: It is possible that the asset does no longer exist when this hook is called.
fn died(asset: AssetId, who: &AccountId);
}
impl<AssetId, AccountId, Balance> FrozenBalance<AssetId, AccountId, Balance> for () {
fn frozen_balance(_: AssetId, _: &AccountId) -> Option<Balance> {
None
}
fn died(_: AssetId, _: &AccountId) {}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub(super) struct TransferFlags {
/// The debited account must stay alive at the end of the operation; an error is returned if
/// this cannot be achieved legally.
pub(super) keep_alive: bool,
/// Less than the amount specified needs be debited by the operation for it to be considered
/// successful. If `false`, then the amount debited will always be at least the amount
/// specified.
pub(super) best_effort: bool,
/// Any additional funds debited (due to minimum balance requirements) should be burned rather
/// than credited to the destination account.
pub(super) burn_dust: bool,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct DebitFlags {
/// The debited account must stay alive at the end of the operation; an error is returned if
/// this cannot be achieved legally.
pub keep_alive: bool,
/// Less than the amount specified needs be debited by the operation for it to be considered
/// successful. If `false`, then the amount debited will always be at least the amount
/// specified.
pub best_effort: bool,
}
impl From<TransferFlags> for DebitFlags {
fn from(f: TransferFlags) -> Self {
Self { keep_alive: f.keep_alive, best_effort: f.best_effort }
}
}
/// Possible errors when converting between external and asset balances.
#[derive(Eq, PartialEq, Copy, Clone, RuntimeDebug, Encode, Decode)]
pub enum ConversionError {
/// The external minimum balance must not be zero.
MinBalanceZero,
/// The asset is not present in storage.
AssetMissing,
/// The asset is not sufficient and thus does not have a reliable `min_balance` so it cannot be
/// converted.
AssetNotSufficient,
}
// Type alias for `frame_system`'s account id.
type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
// This pallet's asset id and balance type.
type AssetIdOf<T, I> = <T as Config<I>>::AssetId;
type AssetBalanceOf<T, I> = <T as Config<I>>::Balance;
// Generic fungible balance type.
type BalanceOf<F, T> = <F as fungible::Inspect<AccountIdOf<T>>>::Balance;
/// Converts a balance value into an asset balance based on the ratio between the fungible's
/// minimum balance and the minimum asset balance.
pub struct BalanceToAssetBalance<F, T, CON, I = ()>(PhantomData<(F, T, CON, I)>);
impl<F, T, CON, I> BalanceConversion<BalanceOf<F, T>, AssetIdOf<T, I>, AssetBalanceOf<T, I>>
for BalanceToAssetBalance<F, T, CON, I>
where
F: fungible::Inspect<AccountIdOf<T>>,
T: Config<I>,
I: 'static,
CON: Convert<BalanceOf<F, T>, AssetBalanceOf<T, I>>,
BalanceOf<F, T>: FixedPointOperand + Zero,
AssetBalanceOf<T, I>: FixedPointOperand + Zero,
{
type Error = ConversionError;
/// Convert the given balance value into an asset balance based on the ratio between the
/// fungible's minimum balance and the minimum asset balance.
///
/// Will return `Err` if the asset is not found, not sufficient or the fungible's minimum
/// balance is zero.
fn to_asset_balance(
balance: BalanceOf<F, T>,
asset_id: AssetIdOf<T, I>,
) -> Result<AssetBalanceOf<T, I>, ConversionError> {
let asset = Asset::<T, I>::get(asset_id).ok_or(ConversionError::AssetMissing)?;
// only sufficient assets have a min balance with reliable value
ensure!(asset.is_sufficient, ConversionError::AssetNotSufficient);
let min_balance = CON::convert(F::minimum_balance());
// make sure we don't divide by zero
ensure!(!min_balance.is_zero(), ConversionError::MinBalanceZero);
let balance = CON::convert(balance);
// balance * asset.min_balance / min_balance
Ok(
FixedU128::saturating_from_rational(asset.min_balance, min_balance)
.saturating_mul_int(balance),
)
}
}
/// Store named reserved balance.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct ReserveData<ReserveIdentifier, Balance> {
/// The identifier for the named reserve.
pub id: ReserveIdentifier,
/// The amount of the named reserve.
pub amount: Balance,
}
#[derive(
Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy,
)]
pub enum AfloatRole {
Owner,
Admin,
BuyerOrSeller,
CPA,
}
impl Default for AfloatRole {
fn default() -> Self {
AfloatRole::BuyerOrSeller
}
}
impl AfloatRole {
pub fn to_vec(self) -> Vec<u8> {
match self {
Self::Owner => "Owner".as_bytes().to_vec(),
Self::Admin => "Admin".as_bytes().to_vec(),
Self::BuyerOrSeller => "BuyerOrSeller".as_bytes().to_vec(),
Self::CPA => "CPA".as_bytes().to_vec(),
}
}
pub fn id(&self) -> [u8; 32] {
self.to_vec().using_encoded(blake2_256)
}
pub fn enum_to_vec() -> Vec<Vec<u8>> {
use crate::types::AfloatRole::*;
[Owner.to_vec(), Admin.to_vec(), BuyerOrSeller.to_vec(), CPA.to_vec()].to_vec()
}
}