use crate::Assets;
use sp_std::result::Result;
use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult, XcmContext};
pub trait TransactAsset {
fn can_check_in(
_origin: &MultiLocation,
_what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
Err(XcmError::Unimplemented)
}
fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
fn can_check_out(
_dest: &MultiLocation,
_what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
Err(XcmError::Unimplemented)
}
fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation, _context: &XcmContext) -> XcmResult {
Err(XcmError::Unimplemented)
}
fn withdraw_asset(
_what: &MultiAsset,
_who: &MultiLocation,
_maybe_context: Option<&XcmContext>,
) -> Result<Assets, XcmError> {
Err(XcmError::Unimplemented)
}
fn internal_transfer_asset(
_asset: &MultiAsset,
_from: &MultiLocation,
_to: &MultiLocation,
_context: &XcmContext,
) -> Result<Assets, XcmError> {
Err(XcmError::Unimplemented)
}
fn transfer_asset(
asset: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
context: &XcmContext,
) -> Result<Assets, XcmError> {
match Self::internal_transfer_asset(asset, from, to, context) {
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => {
let assets = Self::withdraw_asset(asset, from, Some(context))?;
Self::deposit_asset(asset, to, context)?;
Ok(assets)
},
result => result,
}
}
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl TransactAsset for Tuple {
fn can_check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult {
for_tuples!( #(
match Tuple::can_check_in(origin, what, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
log::trace!(
target: "xcm::TransactAsset::can_check_in",
"asset not found: what: {:?}, origin: {:?}, context: {:?}",
what,
origin,
context,
);
Err(XcmError::AssetNotFound)
}
fn check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) {
for_tuples!( #(
Tuple::check_in(origin, what, context);
)* );
}
fn can_check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult {
for_tuples!( #(
match Tuple::can_check_out(dest, what, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
log::trace!(
target: "xcm::TransactAsset::can_check_out",
"asset not found: what: {:?}, dest: {:?}, context: {:?}",
what,
dest,
context,
);
Err(XcmError::AssetNotFound)
}
fn check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) {
for_tuples!( #(
Tuple::check_out(dest, what, context);
)* );
}
fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult {
for_tuples!( #(
match Tuple::deposit_asset(what, who, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
log::trace!(
target: "xcm::TransactAsset::deposit_asset",
"did not deposit asset: what: {:?}, who: {:?}, context: {:?}",
what,
who,
context,
);
Err(XcmError::AssetNotFound)
}
fn withdraw_asset(
what: &MultiAsset,
who: &MultiLocation,
maybe_context: Option<&XcmContext>,
) -> Result<Assets, XcmError> {
for_tuples!( #(
match Tuple::withdraw_asset(what, who, maybe_context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
log::trace!(
target: "xcm::TransactAsset::withdraw_asset",
"did not withdraw asset: what: {:?}, who: {:?}, maybe_context: {:?}",
what,
who,
maybe_context,
);
Err(XcmError::AssetNotFound)
}
fn internal_transfer_asset(
what: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
context: &XcmContext,
) -> Result<Assets, XcmError> {
for_tuples!( #(
match Tuple::internal_transfer_asset(what, from, to, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
log::trace!(
target: "xcm::TransactAsset::internal_transfer_asset",
"did not transfer asset: what: {:?}, from: {:?}, to: {:?}, context: {:?}",
what,
from,
to,
context,
);
Err(XcmError::AssetNotFound)
}
}
#[cfg(test)]
mod tests {
use super::*;
use xcm::latest::Junctions::Here;
pub struct UnimplementedTransactor;
impl TransactAsset for UnimplementedTransactor {}
pub struct NotFoundTransactor;
impl TransactAsset for NotFoundTransactor {
fn can_check_in(
_origin: &MultiLocation,
_what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
Err(XcmError::AssetNotFound)
}
fn can_check_out(
_dest: &MultiLocation,
_what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
Err(XcmError::AssetNotFound)
}
fn deposit_asset(
_what: &MultiAsset,
_who: &MultiLocation,
_context: &XcmContext,
) -> XcmResult {
Err(XcmError::AssetNotFound)
}
fn withdraw_asset(
_what: &MultiAsset,
_who: &MultiLocation,
_context: Option<&XcmContext>,
) -> Result<Assets, XcmError> {
Err(XcmError::AssetNotFound)
}
fn internal_transfer_asset(
_what: &MultiAsset,
_from: &MultiLocation,
_to: &MultiLocation,
_context: &XcmContext,
) -> Result<Assets, XcmError> {
Err(XcmError::AssetNotFound)
}
}
pub struct OverflowTransactor;
impl TransactAsset for OverflowTransactor {
fn can_check_in(
_origin: &MultiLocation,
_what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
Err(XcmError::Overflow)
}
fn can_check_out(
_dest: &MultiLocation,
_what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
Err(XcmError::Overflow)
}
fn deposit_asset(
_what: &MultiAsset,
_who: &MultiLocation,
_context: &XcmContext,
) -> XcmResult {
Err(XcmError::Overflow)
}
fn withdraw_asset(
_what: &MultiAsset,
_who: &MultiLocation,
_context: Option<&XcmContext>,
) -> Result<Assets, XcmError> {
Err(XcmError::Overflow)
}
fn internal_transfer_asset(
_what: &MultiAsset,
_from: &MultiLocation,
_to: &MultiLocation,
_context: &XcmContext,
) -> Result<Assets, XcmError> {
Err(XcmError::Overflow)
}
}
pub struct SuccessfulTransactor;
impl TransactAsset for SuccessfulTransactor {
fn can_check_in(
_origin: &MultiLocation,
_what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
Ok(())
}
fn can_check_out(
_dest: &MultiLocation,
_what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
Ok(())
}
fn deposit_asset(
_what: &MultiAsset,
_who: &MultiLocation,
_context: &XcmContext,
) -> XcmResult {
Ok(())
}
fn withdraw_asset(
_what: &MultiAsset,
_who: &MultiLocation,
_context: Option<&XcmContext>,
) -> Result<Assets, XcmError> {
Ok(Assets::default())
}
fn internal_transfer_asset(
_what: &MultiAsset,
_from: &MultiLocation,
_to: &MultiLocation,
_context: &XcmContext,
) -> Result<Assets, XcmError> {
Ok(Assets::default())
}
}
#[test]
fn defaults_to_asset_not_found() {
type MultiTransactor =
(UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor);
assert_eq!(
MultiTransactor::deposit_asset(
&(Here, 1u128).into(),
&Here.into(),
&XcmContext::with_message_id([0; 32]),
),
Err(XcmError::AssetNotFound)
);
}
#[test]
fn unimplemented_and_not_found_continue_iteration() {
type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor);
assert_eq!(
MultiTransactor::deposit_asset(
&(Here, 1u128).into(),
&Here.into(),
&XcmContext::with_message_id([0; 32]),
),
Ok(())
);
}
#[test]
fn unexpected_error_stops_iteration() {
type MultiTransactor = (OverflowTransactor, SuccessfulTransactor);
assert_eq!(
MultiTransactor::deposit_asset(
&(Here, 1u128).into(),
&Here.into(),
&XcmContext::with_message_id([0; 32]),
),
Err(XcmError::Overflow)
);
}
#[test]
fn success_stops_iteration() {
type MultiTransactor = (SuccessfulTransactor, OverflowTransactor);
assert_eq!(
MultiTransactor::deposit_asset(
&(Here, 1u128).into(),
&Here.into(),
&XcmContext::with_message_id([0; 32]),
),
Ok(()),
);
}
}