use frame_support::traits::ExistenceRequirement;
use parity_scale_codec::FullCodec;
use sp_runtime::traits::{Convert, MaybeSerializeDeserialize, SaturatedConversion};
use sp_std::{
cmp::{Eq, PartialEq},
fmt::Debug,
marker::PhantomData,
prelude::*,
result,
};
use orml_xcm_support::{OnDepositFail, UnknownAsset as UnknownAssetT};
use xcm::v5::{prelude::*, Asset, Error as XcmError, Location, Result};
use xcm_executor::{
traits::{ConvertLocation, MatchesFungible, TransactAsset},
AssetsInHolding,
};
enum Error {
FailedToMatchFungible,
AccountIdConversionFailed,
CurrencyIdConversionFailed,
}
impl From<Error> for XcmError {
fn from(e: Error) -> Self {
match e {
Error::FailedToMatchFungible => XcmError::FailedToTransactAsset("FailedToMatchFungible"),
Error::AccountIdConversionFailed => XcmError::FailedToTransactAsset("AccountIdConversionFailed"),
Error::CurrencyIdConversionFailed => XcmError::FailedToTransactAsset("CurrencyIdConversionFailed"),
}
}
}
#[allow(clippy::type_complexity)]
pub struct MultiTeleportCurrencyAdapter<
MultiCurrency,
UnknownAsset,
Match,
AccountId,
AccountIdConvert,
CurrencyId,
CurrencyIdConvert,
DepositFailureHandler,
>(
PhantomData<(
MultiCurrency,
UnknownAsset,
Match,
AccountId,
AccountIdConvert,
CurrencyId,
CurrencyIdConvert,
DepositFailureHandler,
)>,
);
impl<
MultiCurrency: orml_traits::MultiCurrency<AccountId, CurrencyId = CurrencyId>,
UnknownAsset: UnknownAssetT,
Match: MatchesFungible<MultiCurrency::Balance>,
AccountId: sp_std::fmt::Debug + Clone,
AccountIdConvert: ConvertLocation<AccountId>,
CurrencyId: FullCodec + Eq + PartialEq + Copy + MaybeSerializeDeserialize + Debug,
CurrencyIdConvert: Convert<Asset, Option<CurrencyId>>,
DepositFailureHandler: OnDepositFail<CurrencyId, AccountId, MultiCurrency::Balance>,
> TransactAsset
for MultiTeleportCurrencyAdapter<
MultiCurrency,
UnknownAsset,
Match,
AccountId,
AccountIdConvert,
CurrencyId,
CurrencyIdConvert,
DepositFailureHandler,
>
{
fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> Result {
Ok(())
}
fn check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) {}
fn deposit_asset(asset: &Asset, location: &Location, _context: Option<&XcmContext>) -> Result {
match (
AccountIdConvert::convert_location(location),
CurrencyIdConvert::convert(asset.clone()),
Match::matches_fungible(asset),
) {
(Some(who), Some(currency_id), Some(amount)) => MultiCurrency::deposit(currency_id, &who, amount)
.or_else(|err| DepositFailureHandler::on_deposit_currency_fail(err, currency_id, &who, amount)),
_ => UnknownAsset::deposit(asset, location)
.or_else(|err| DepositFailureHandler::on_deposit_unknown_asset_fail(err, asset, location)),
}
}
fn withdraw_asset(
asset: &Asset,
location: &Location,
_maybe_context: Option<&XcmContext>,
) -> result::Result<AssetsInHolding, XcmError> {
UnknownAsset::withdraw(asset, location).or_else(|_| {
let who = AccountIdConvert::convert_location(location)
.ok_or_else(|| XcmError::from(Error::AccountIdConversionFailed))?;
let currency_id = CurrencyIdConvert::convert(asset.clone())
.ok_or_else(|| XcmError::from(Error::CurrencyIdConversionFailed))?;
let amount: MultiCurrency::Balance = Match::matches_fungible(asset)
.ok_or_else(|| XcmError::from(Error::FailedToMatchFungible))?
.saturated_into();
MultiCurrency::withdraw(currency_id, &who, amount, ExistenceRequirement::AllowDeath)
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))
})?;
Ok(asset.clone().into())
}
}