use cfg_traits::PreConditions;
use frame_support::{
defensive,
traits::{
fungible,
fungibles::{Dust, Inspect, InspectHold, Mutate, MutateHold, Unbalanced},
tokens::{
DepositConsequence, Fortitude, Precision, Preservation, Provenance, Restriction,
WithdrawConsequence,
},
},
};
use super::*;
pub enum FungiblesInspectEffects<AssetId, AccountId, Balance> {
ReducibleBalance(AssetId, AccountId, Preservation, Fortitude, Balance),
}
pub struct FungiblesInspectPassthrough;
impl<AssetId, AccountId, Balance>
PreConditions<FungiblesInspectEffects<AssetId, AccountId, Balance>>
for FungiblesInspectPassthrough
{
type Result = Balance;
fn check(t: FungiblesInspectEffects<AssetId, AccountId, Balance>) -> Self::Result {
match t {
FungiblesInspectEffects::ReducibleBalance(_, _, _, _, amount) => amount,
}
}
}
impl<T: Config> Inspect<T::AccountId> for Pallet<T> {
type AssetId = T::CurrencyId;
type Balance = T::Balance;
fn total_issuance(asset: Self::AssetId) -> Self::Balance {
if asset == T::NativeToken::get() {
<Self as fungible::Inspect<T::AccountId>>::total_issuance()
} else {
<T::Fungibles as Inspect<T::AccountId>>::total_issuance(asset)
}
}
fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
if asset == T::NativeToken::get() {
<Self as fungible::Inspect<T::AccountId>>::minimum_balance()
} else {
<T::Fungibles as Inspect<T::AccountId>>::minimum_balance(asset)
}
}
fn total_balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
if asset == T::NativeToken::get() {
<Self as fungible::Inspect<T::AccountId>>::total_balance(who)
} else {
<T::Fungibles as Inspect<T::AccountId>>::total_balance(asset, who)
}
}
fn balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
if asset == T::NativeToken::get() {
<Self as fungible::Inspect<T::AccountId>>::balance(who)
} else {
<T::Fungibles as Inspect<T::AccountId>>::balance(asset, who)
}
}
fn reducible_balance(
asset: Self::AssetId,
who: &T::AccountId,
preservation: Preservation,
force: Fortitude,
) -> Self::Balance {
if asset == T::NativeToken::get() {
<Self as fungible::Inspect<T::AccountId>>::reducible_balance(who, preservation, force)
} else {
T::PreFungiblesInspect::check(FungiblesInspectEffects::ReducibleBalance(
asset,
who.clone(),
preservation,
force,
<T::Fungibles as Inspect<T::AccountId>>::reducible_balance(
asset,
who,
preservation,
force,
),
))
}
}
fn can_deposit(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
provenance: Provenance,
) -> DepositConsequence {
if asset == T::NativeToken::get() {
<Self as fungible::Inspect<T::AccountId>>::can_deposit(who, amount, provenance)
} else {
<T::Fungibles as Inspect<T::AccountId>>::can_deposit(asset, who, amount, provenance)
}
}
fn can_withdraw(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
) -> WithdrawConsequence<Self::Balance> {
if asset == T::NativeToken::get() {
<Self as fungible::Inspect<T::AccountId>>::can_withdraw(who, amount)
} else {
<T::Fungibles as Inspect<T::AccountId>>::can_withdraw(asset, who, amount)
}
}
fn asset_exists(asset: Self::AssetId) -> bool {
<T::Fungibles as Inspect<T::AccountId>>::asset_exists(asset)
}
}
pub enum FungiblesInspectHoldEffects<AssetId, AccountId, Balance> {
CanHold(AssetId, AccountId, Balance, bool),
HoldAvailable(AssetId, AccountId, bool),
}
impl<T: Config> InspectHold<T::AccountId> for Pallet<T> {
type Reason = ();
fn total_balance_on_hold(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance {
if asset == T::NativeToken::get() {
<Self as fungible::InspectHold<T::AccountId>>::total_balance_on_hold(who)
} else {
<T::Fungibles as InspectHold<T::AccountId>>::total_balance_on_hold(asset, who)
}
}
fn reducible_total_balance_on_hold(
asset: Self::AssetId,
who: &T::AccountId,
force: Fortitude,
) -> Self::Balance {
if asset == T::NativeToken::get() {
<Self as fungible::InspectHold<T::AccountId>>::reducible_total_balance_on_hold(
who, force,
)
} else {
<T::Fungibles as InspectHold<T::AccountId>>::reducible_total_balance_on_hold(
asset, who, force,
)
}
}
fn balance_on_hold(
asset: Self::AssetId,
_reason: &Self::Reason,
who: &T::AccountId,
) -> Self::Balance {
if asset == T::NativeToken::get() {
<Self as fungible::InspectHold<T::AccountId>>::balance_on_hold(
&HoldReason::NativeIndex.into(),
who,
)
} else {
<T::Fungibles as InspectHold<T::AccountId>>::balance_on_hold(asset, &(), who)
}
}
fn hold_available(asset: Self::AssetId, _reason: &Self::Reason, who: &T::AccountId) -> bool {
if asset == T::NativeToken::get() {
<Self as fungible::InspectHold<T::AccountId>>::hold_available(
&HoldReason::NativeIndex.into(),
who,
)
} else {
let hold_available =
<T::Fungibles as InspectHold<T::AccountId>>::hold_available(asset, &(), who);
T::PreFungiblesInspectHold::check(FungiblesInspectHoldEffects::HoldAvailable(
asset,
who.clone(),
hold_available,
)) && hold_available
}
}
fn can_hold(
asset: Self::AssetId,
_reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
) -> bool {
if asset == T::NativeToken::get() {
<Self as fungible::InspectHold<T::AccountId>>::can_hold(
&HoldReason::NativeIndex.into(),
who,
amount,
)
} else {
let can_hold =
<T::Fungibles as InspectHold<T::AccountId>>::can_hold(asset, &(), who, amount);
T::PreFungiblesInspectHold::check(FungiblesInspectHoldEffects::CanHold(
asset,
who.clone(),
amount,
can_hold,
)) && can_hold
}
}
}
pub enum FungiblesMutateEffects<AssetId, AccountId, Balance> {
MintInto(AssetId, AccountId, Balance),
BurnFrom(AssetId, AccountId, Balance),
}
impl<T: Config> Mutate<T::AccountId> for Pallet<T> {
fn mint_into(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
) -> Result<Self::Balance, DispatchError> {
if asset == T::NativeToken::get() {
<Self as fungible::Mutate<T::AccountId>>::mint_into(who, amount)
} else {
ensure!(
T::PreFungiblesMutate::check(FungiblesMutateEffects::MintInto(
asset,
who.clone(),
amount
)),
Error::<T>::PreConditionsNotMet
);
<T::Fungibles as Mutate<T::AccountId>>::mint_into(asset, who, amount)
}
}
fn burn_from(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
precision: Precision,
force: Fortitude,
) -> Result<Self::Balance, DispatchError> {
if asset == T::NativeToken::get() {
<Self as fungible::Mutate<T::AccountId>>::burn_from(who, amount, precision, force)
} else {
ensure!(
T::PreFungiblesMutate::check(FungiblesMutateEffects::BurnFrom(
asset,
who.clone(),
amount
)),
Error::<T>::PreConditionsNotMet
);
<T::Fungibles as Mutate<T::AccountId>>::burn_from(asset, who, amount, precision, force)
}
}
fn transfer(
asset: Self::AssetId,
source: &T::AccountId,
dest: &T::AccountId,
amount: Self::Balance,
preservation: Preservation,
) -> Result<Self::Balance, DispatchError> {
if asset == T::NativeToken::get() {
<Self as fungible::Mutate<T::AccountId>>::transfer(source, dest, amount, preservation)
} else {
ensure!(
T::PreFungiblesTransfer::check(FungiblesTransferEffects::Transfer(
asset,
source.clone(),
dest.clone(),
amount,
preservation != Preservation::Expendable,
)),
Error::<T>::PreConditionsNotMet
);
<T::Fungibles as Mutate<T::AccountId>>::transfer(
asset,
source,
dest,
amount,
preservation,
)
}
}
}
pub enum FungiblesMutateHoldEffects<AssetId, AccountId, Balance> {
Hold(AssetId, AccountId, Balance),
Release(AssetId, AccountId, Balance, bool),
TransferHeld(AssetId, AccountId, AccountId, Balance, bool, bool),
}
impl<T: Config> fungibles::hold::Unbalanced<T::AccountId> for Pallet<T> {
fn set_balance_on_hold(
asset: Self::AssetId,
_reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
) -> sp_runtime::DispatchResult {
if asset == T::NativeToken::get() {
<Self as fungible::hold::Unbalanced<T::AccountId>>::set_balance_on_hold(
&HoldReason::NativeIndex.into(),
who,
amount,
)
} else {
ensure!(
T::PreFungiblesMutateHold::check(FungiblesMutateHoldEffects::Hold(
asset,
who.clone(),
amount
)),
Error::<T>::PreConditionsNotMet
);
<T::Fungibles as fungibles::hold::Unbalanced<T::AccountId>>::set_balance_on_hold(
asset,
&(),
who,
amount,
)
}
}
}
impl<T: Config> MutateHold<T::AccountId> for Pallet<T> {
fn hold(
asset: Self::AssetId,
_reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
) -> DispatchResult {
if asset == T::NativeToken::get() {
<Self as fungible::MutateHold<T::AccountId>>::hold(
&HoldReason::NativeIndex.into(),
who,
amount,
)
} else {
ensure!(
T::PreFungiblesMutateHold::check(FungiblesMutateHoldEffects::Hold(
asset,
who.clone(),
amount
)),
Error::<T>::PreConditionsNotMet
);
<T::Fungibles as MutateHold<T::AccountId>>::hold(asset, &(), who, amount)
}
}
fn release(
asset: Self::AssetId,
_reason: &Self::Reason,
who: &T::AccountId,
amount: Self::Balance,
precision: Precision,
) -> Result<Self::Balance, DispatchError> {
if asset == T::NativeToken::get() {
<Self as fungible::MutateHold<T::AccountId>>::release(
&HoldReason::NativeIndex.into(),
who,
amount,
precision,
)
} else {
ensure!(
T::PreFungiblesMutateHold::check(FungiblesMutateHoldEffects::Release(
asset,
who.clone(),
amount,
precision == Precision::BestEffort,
)),
Error::<T>::PreConditionsNotMet
);
<T::Fungibles as MutateHold<T::AccountId>>::release(asset, &(), who, amount, precision)
}
}
fn transfer_on_hold(
asset: Self::AssetId,
_reason: &Self::Reason,
source: &T::AccountId,
dest: &T::AccountId,
amount: Self::Balance,
precision: Precision,
mode: Restriction,
force: Fortitude,
) -> Result<Self::Balance, DispatchError> {
if asset == T::NativeToken::get() {
<Self as fungible::MutateHold<T::AccountId>>::transfer_on_hold(
&HoldReason::NativeIndex.into(),
source,
dest,
amount,
precision,
mode,
force,
)
} else {
ensure!(
T::PreFungiblesMutateHold::check(FungiblesMutateHoldEffects::TransferHeld(
asset,
source.clone(),
dest.clone(),
amount,
precision == Precision::BestEffort,
mode == Restriction::OnHold,
)),
Error::<T>::PreConditionsNotMet
);
<T::Fungibles as MutateHold<T::AccountId>>::transfer_on_hold(
asset,
&(),
source,
dest,
amount,
precision,
mode,
force,
)
}
}
}
pub enum FungiblesTransferEffects<AssetId, AccountId, Balance> {
Transfer(AssetId, AccountId, AccountId, Balance, bool),
}
pub enum FungiblesUnbalancedEffects<AssetId, AccountId, Balance> {
WriteBalance(AssetId, AccountId, Balance),
SetTotalIssuance(AssetId, Balance),
}
impl<T: Config> Unbalanced<T::AccountId> for Pallet<T> {
fn handle_dust(_dust: Dust<T::AccountId, Self>) {
defensive!("DustRemoval disabled");
}
fn write_balance(
asset: Self::AssetId,
who: &T::AccountId,
amount: Self::Balance,
) -> Result<Option<Self::Balance>, DispatchError> {
if asset == T::NativeToken::get() {
<Self as fungible::Unbalanced<T::AccountId>>::write_balance(who, amount)
} else {
ensure!(
T::PreFungiblesUnbalanced::check(FungiblesUnbalancedEffects::WriteBalance(
asset,
who.clone(),
amount
)),
Error::<T>::PreConditionsNotMet
);
<Self as fungibles::Unbalanced<T::AccountId>>::write_balance(asset, who, amount)
}
}
fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) {
if asset == T::NativeToken::get() {
<Self as fungible::Unbalanced<T::AccountId>>::set_total_issuance(amount)
} else if T::PreFungiblesUnbalanced::check(FungiblesUnbalancedEffects::SetTotalIssuance(
asset, amount,
)) {
<Self as fungibles::Unbalanced<T::AccountId>>::set_total_issuance(asset, amount)
}
}
}