use super::*;
use frame_support::{
pallet_prelude::*,
traits::{fungible, tokens::ConversionToAssetBalance},
};
use sp_runtime::{traits::Convert, FixedPointNumber, FixedU128};
pub type DepositBalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
pub type AssetAccountOf<T, I> = AssetAccount<
<T as Config<I>>::Balance,
DepositBalanceOf<T, I>,
<T as Config<I>>::Extra,
<T as SystemConfig>::AccountId,
>;
pub type ExistenceReasonOf<T, I> =
ExistenceReason<DepositBalanceOf<T, I>, <T as SystemConfig>::AccountId>;
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)]
pub enum AssetStatus {
Live,
Frozen,
Destroying,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)]
pub struct AssetDetails<Balance, AccountId, DepositBalance> {
pub owner: AccountId,
pub issuer: AccountId,
pub admin: AccountId,
pub freezer: AccountId,
pub supply: Balance,
pub deposit: DepositBalance,
pub min_balance: Balance,
pub is_sufficient: bool,
pub accounts: u32,
pub sufficients: u32,
pub approvals: u32,
pub status: AssetStatus,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, Default, MaxEncodedLen, TypeInfo)]
pub struct Approval<Balance, DepositBalance> {
pub amount: Balance,
pub 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, Debug, MaxEncodedLen, TypeInfo)]
pub enum ExistenceReason<Balance, AccountId> {
#[codec(index = 0)]
Consumer,
#[codec(index = 1)]
Sufficient,
#[codec(index = 2)]
DepositHeld(Balance),
#[codec(index = 3)]
DepositRefunded,
#[codec(index = 4)]
DepositFrom(AccountId, Balance),
}
impl<Balance, AccountId> ExistenceReason<Balance, AccountId>
where
AccountId: Clone,
{
pub fn take_deposit(&mut self) -> Option<Balance> {
if !matches!(self, ExistenceReason::DepositHeld(_)) {
return None;
}
if let ExistenceReason::DepositHeld(deposit) =
core::mem::replace(self, ExistenceReason::DepositRefunded)
{
Some(deposit)
} else {
None
}
}
pub fn take_deposit_from(&mut self) -> Option<(AccountId, Balance)> {
if !matches!(self, ExistenceReason::DepositFrom(..)) {
return None;
}
if let ExistenceReason::DepositFrom(depositor, deposit) =
core::mem::replace(self, ExistenceReason::DepositRefunded)
{
Some((depositor, deposit))
} else {
None
}
}
}
#[test]
fn ensure_bool_decodes_to_liquid_or_frozen() {
assert_eq!(false.encode(), AccountStatus::Liquid.encode());
assert_eq!(true.encode(), AccountStatus::Frozen.encode());
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)]
pub enum AccountStatus {
Liquid,
Frozen,
Blocked,
}
impl AccountStatus {
pub fn is_frozen(&self) -> bool {
matches!(self, AccountStatus::Frozen | AccountStatus::Blocked)
}
pub fn is_blocked(&self) -> bool {
matches!(self, AccountStatus::Blocked)
}
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)]
pub struct AssetAccount<Balance, DepositBalance, Extra, AccountId> {
pub balance: Balance,
pub status: AccountStatus,
pub reason: ExistenceReason<DepositBalance, AccountId>,
pub extra: Extra,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, Default, Debug, MaxEncodedLen, TypeInfo)]
pub struct AssetMetadata<DepositBalance, BoundedString> {
pub deposit: DepositBalance,
pub name: BoundedString,
pub symbol: BoundedString,
pub decimals: u8,
pub is_frozen: bool,
}
pub trait FrozenBalance<AssetId, AccountId, Balance> {
fn frozen_balance(asset: AssetId, who: &AccountId) -> Option<Balance>;
fn died(asset: AssetId, who: &AccountId);
fn contains_freezes(asset: AssetId) -> bool;
}
impl<AssetId, AccountId, Balance> FrozenBalance<AssetId, AccountId, Balance> for () {
fn frozen_balance(_: AssetId, _: &AccountId) -> Option<Balance> {
None
}
fn died(_: AssetId, _: &AccountId) {}
fn contains_freezes(_: AssetId) -> bool {
false
}
}
pub trait BalanceOnHold<AssetId, AccountId, Balance> {
fn balance_on_hold(asset: AssetId, who: &AccountId) -> Option<Balance>;
fn died(asset: AssetId, who: &AccountId);
fn contains_holds(asset: AssetId) -> bool;
}
impl<AssetId, AccountId, Balance> BalanceOnHold<AssetId, AccountId, Balance> for () {
fn balance_on_hold(_: AssetId, _: &AccountId) -> Option<Balance> {
None
}
fn died(_: AssetId, _: &AccountId) {}
fn contains_holds(_: AssetId) -> bool {
false
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct TransferFlags {
pub keep_alive: bool,
pub best_effort: bool,
pub burn_dust: bool,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct DebitFlags {
pub keep_alive: bool,
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 }
}
}
#[derive(Eq, PartialEq, Copy, Clone, Debug, Encode, Decode)]
pub enum ConversionError {
MinBalanceZero,
AssetMissing,
AssetNotSufficient,
}
type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
type AssetIdOf<T, I> = <T as Config<I>>::AssetId;
type AssetBalanceOf<T, I> = <T as Config<I>>::Balance;
type BalanceOf<F, T> = <F as fungible::Inspect<AccountIdOf<T>>>::Balance;
pub struct BalanceToAssetBalance<F, T, CON, I = ()>(PhantomData<(F, T, CON, I)>);
impl<F, T, CON, I> ConversionToAssetBalance<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>>,
{
type Error = ConversionError;
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)?;
ensure!(asset.is_sufficient, ConversionError::AssetNotSufficient);
let min_balance = CON::convert(F::minimum_balance());
ensure!(!min_balance.is_zero(), ConversionError::MinBalanceZero);
let balance = CON::convert(balance);
Ok(FixedU128::saturating_from_rational(asset.min_balance, min_balance)
.saturating_mul_int(balance))
}
}