#![allow(deprecated)]
use crate::InspectMessageQueues;
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode};
use core::{convert::TryInto, marker::PhantomData};
use pezframe_support::{ensure, traits::Get};
use xcm::prelude::*;
use xcm_executor::traits::{validate_export, ExportXcm};
use SendError::*;
pub fn ensure_is_remote(
universal_local: impl Into<InteriorLocation>,
dest: impl Into<Location>,
) -> Result<(NetworkId, InteriorLocation), Location> {
let dest = dest.into();
let universal_local = universal_local.into();
let local_net = match universal_local.global_consensus() {
Ok(x) => x,
Err(_) => return Err(dest),
};
let universal_destination: InteriorLocation = universal_local
.into_location()
.appended_with(dest.clone())
.map_err(|x| x.1)?
.try_into()?;
let (remote_dest, remote_net) = match universal_destination.split_first() {
(d, Some(GlobalConsensus(n))) if n != local_net => (d, n),
_ => return Err(dest),
};
Ok((remote_net, remote_dest))
}
#[deprecated(note = "Will be removed after July 2025; It uses hard-coded channel `0`, \
use `xcm_builder::LocalExporter` directly instead.")]
pub struct UnpaidLocalExporter<Exporter, UniversalLocation>(
PhantomData<(Exporter, UniversalLocation)>,
);
impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
for UnpaidLocalExporter<Exporter, UniversalLocation>
{
type Ticket = Exporter::Ticket;
fn validate(
dest: &mut Option<Location>,
msg: &mut Option<Xcm<()>>,
) -> SendResult<Exporter::Ticket> {
let d = dest.clone().ok_or(MissingArgument)?;
let universal_source = UniversalLocation::get();
let devolved = ensure_is_remote(universal_source.clone(), d).map_err(|error| {
tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to devolve location");
NotApplicable
})?;
let (remote_network, remote_location) = devolved;
let xcm = msg.take().ok_or(MissingArgument)?;
validate_export::<Exporter>(
remote_network,
0,
universal_source,
remote_location,
xcm.clone(),
)
.inspect_err(|err| {
if let NotApplicable = err {
*msg = Some(xcm);
}
})
}
fn deliver(ticket: Exporter::Ticket) -> Result<XcmHash, SendError> {
Exporter::deliver(ticket)
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful_delivery(_: Option<Location>) {}
}
pub struct LocalExporter<Exporter, UniversalLocation>(PhantomData<(Exporter, UniversalLocation)>);
impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
for LocalExporter<Exporter, UniversalLocation>
{
type Ticket = Exporter::Ticket;
fn validate(
dest: &mut Option<Location>,
msg: &mut Option<Xcm<()>>,
) -> SendResult<Exporter::Ticket> {
let d = dest.clone().ok_or(MissingArgument)?;
let universal_source = UniversalLocation::get();
let devolved = ensure_is_remote(universal_source.clone(), d).map_err(|error| {
tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to devolve location");
NotApplicable
})?;
let (remote_network, remote_location) = devolved;
let xcm = msg.take().ok_or(MissingArgument)?;
let hash =
(Some(Location::here()), &remote_location).using_encoded(pezsp_io::hashing::blake2_128);
let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0);
validate_export::<Exporter>(
remote_network,
channel,
universal_source,
remote_location,
xcm.clone(),
)
.inspect_err(|err| {
if let NotApplicable = err {
*msg = Some(xcm);
}
})
}
fn deliver(ticket: Exporter::Ticket) -> Result<XcmHash, SendError> {
Exporter::deliver(ticket)
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful_delivery(_: Option<Location>) {}
}
pub trait ExporterFor {
fn exporter_for(
network: &NetworkId,
remote_location: &InteriorLocation,
message: &Xcm<()>,
) -> Option<(Location, Option<Asset>)>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl ExporterFor for Tuple {
fn exporter_for(
network: &NetworkId,
remote_location: &InteriorLocation,
message: &Xcm<()>,
) -> Option<(Location, Option<Asset>)> {
for_tuples!( #(
if let Some(r) = Tuple::exporter_for(network, remote_location, message) {
return Some(r);
}
)* );
None
}
}
pub struct NetworkExportTableItem {
pub remote_network: NetworkId,
pub remote_location_filter: Option<Vec<InteriorLocation>>,
pub bridge: Location,
pub payment: Option<Asset>,
}
impl NetworkExportTableItem {
pub fn new(
remote_network: NetworkId,
remote_location_filter: Option<Vec<InteriorLocation>>,
bridge: Location,
payment: Option<Asset>,
) -> Self {
Self { remote_network, remote_location_filter, bridge, payment }
}
}
pub struct NetworkExportTable<T>(core::marker::PhantomData<T>);
impl<T: Get<Vec<NetworkExportTableItem>>> ExporterFor for NetworkExportTable<T> {
fn exporter_for(
network: &NetworkId,
remote_location: &InteriorLocation,
_: &Xcm<()>,
) -> Option<(Location, Option<Asset>)> {
T::get()
.into_iter()
.find(|item| {
&item.remote_network == network
&& item
.remote_location_filter
.as_ref()
.map(|filters| filters.iter().any(|filter| filter == remote_location))
.unwrap_or(true)
})
.map(|item| (item.bridge, item.payment))
}
}
pub struct UnpaidRemoteExporter<Bridges, Router, UniversalLocation>(
PhantomData<(Bridges, Router, UniversalLocation)>,
);
impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
for UnpaidRemoteExporter<Bridges, Router, UniversalLocation>
{
type Ticket = Router::Ticket;
fn validate(
dest: &mut Option<Location>,
msg: &mut Option<Xcm<()>>,
) -> SendResult<Router::Ticket> {
let d = dest.clone().ok_or(MissingArgument)?;
let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|error| {
tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to devolve location");
NotApplicable
})?;
let (remote_network, remote_location) = devolved;
let xcm = msg.take().ok_or(MissingArgument)?;
let Some((bridge, maybe_payment)) =
Bridges::exporter_for(&remote_network, &remote_location, &xcm)
else {
*msg = Some(xcm);
return Err(NotApplicable);
};
let maybe_forward_id = match xcm.last() {
Some(SetTopic(t)) => Some(*t),
_ => None,
};
let mut message = Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
ExportMessage {
network: remote_network,
destination: remote_location,
xcm: xcm.clone(),
},
]);
if let Some(forward_id) = maybe_forward_id {
message.0.push(SetTopic(forward_id));
}
let (v, mut cost) = validate_send::<Router>(bridge, message).inspect_err(|err| {
if let NotApplicable = err {
*msg = Some(xcm);
}
})?;
if let Some(bridge_payment) = maybe_payment {
cost.push(bridge_payment);
}
Ok((v, cost))
}
fn deliver(validation: Self::Ticket) -> Result<XcmHash, SendError> {
Router::deliver(validation)
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful_delivery(location: Option<Location>) {
Router::ensure_successful_delivery(location);
}
}
impl<Bridges, Router, UniversalLocation> InspectMessageQueues
for UnpaidRemoteExporter<Bridges, Router, UniversalLocation>
{
fn clear_messages() {}
fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
Vec::new()
}
}
pub struct SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>(
PhantomData<(Bridges, Router, UniversalLocation)>,
);
impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
for SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>
{
type Ticket = Router::Ticket;
fn validate(
dest: &mut Option<Location>,
msg: &mut Option<Xcm<()>>,
) -> SendResult<Router::Ticket> {
let d = dest.clone().ok_or(MissingArgument)?;
let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|error| {
tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to devolve location");
NotApplicable
})?;
let (remote_network, remote_location) = devolved;
let xcm = msg.take().ok_or(MissingArgument)?;
let Some((bridge, maybe_payment)) =
Bridges::exporter_for(&remote_network, &remote_location, &xcm)
else {
*msg = Some(xcm);
return Err(NotApplicable);
};
let maybe_forward_id = match xcm.last() {
Some(SetTopic(t)) => Some(*t),
_ => None,
};
let local_from_bridge = UniversalLocation::get().invert_target(&bridge).map_err(|_| {
tracing::debug!(target: "xcm::universal_exports", "Failed to invert bridge location");
Unroutable
})?;
let export_instruction = ExportMessage {
network: remote_network,
destination: remote_location,
xcm: xcm.clone(),
};
let mut message = Xcm(if let Some(ref payment) = maybe_payment {
let fees =
payment.clone().reanchored(&bridge, &UniversalLocation::get()).map_err(|_| {
tracing::debug!(target: "xcm::universal_exports", "Failed to reanchor payment");
Unroutable
})?;
vec![
WithdrawAsset(fees.clone().into()),
BuyExecution { fees, weight_limit: Unlimited },
SetAppendix(Xcm(vec![DepositAsset {
assets: AllCounted(1).into(),
beneficiary: local_from_bridge,
}])),
export_instruction,
]
} else {
vec![export_instruction]
});
if let Some(forward_id) = maybe_forward_id {
message.0.push(SetTopic(forward_id));
}
let (v, mut cost) = validate_send::<Router>(bridge, message).inspect_err(|err| {
if let NotApplicable = err {
*msg = Some(xcm);
}
})?;
if let Some(bridge_payment) = maybe_payment {
cost.push(bridge_payment);
}
Ok((v, cost))
}
fn deliver(ticket: Router::Ticket) -> Result<XcmHash, SendError> {
Router::deliver(ticket)
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful_delivery(location: Option<Location>) {
Router::ensure_successful_delivery(location);
}
}
impl<Bridges, Router, UniversalLocation> InspectMessageQueues
for SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>
{
fn clear_messages() {}
fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
Vec::new()
}
}
pub trait DispatchBlob {
fn dispatch_blob(blob: Vec<u8>) -> Result<(), DispatchBlobError>;
}
pub trait HaulBlob {
fn haul_blob(blob: Vec<u8>) -> Result<(), HaulBlobError>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HaulBlobError {
Transport(&'static str),
}
impl From<HaulBlobError> for SendError {
fn from(err: HaulBlobError) -> Self {
match err {
HaulBlobError::Transport(reason) => SendError::Transport(reason),
}
}
}
#[derive(Clone, Encode, Decode)]
pub struct BridgeMessage {
pub universal_dest: VersionedInteriorLocation,
pub message: VersionedXcm<()>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DispatchBlobError {
Unbridgable,
InvalidEncoding,
UnsupportedLocationVersion,
UnsupportedXcmVersion,
RoutingError,
NonUniversalDestination,
WrongGlobal,
}
pub struct BridgeBlobDispatcher<Router, OurPlace, OurPlaceBridgeInstance>(
PhantomData<(Router, OurPlace, OurPlaceBridgeInstance)>,
);
impl<
Router: SendXcm,
OurPlace: Get<InteriorLocation>,
OurPlaceBridgeInstance: Get<Option<InteriorLocation>>,
> DispatchBlob for BridgeBlobDispatcher<Router, OurPlace, OurPlaceBridgeInstance>
{
fn dispatch_blob(blob: Vec<u8>) -> Result<(), DispatchBlobError> {
let our_universal = OurPlace::get();
let our_global = our_universal.global_consensus().map_err(|()| {
tracing::debug!(target: "xcm::universal_exports", "Failed to get global consensus");
DispatchBlobError::Unbridgable
})?;
let BridgeMessage { universal_dest, message } =
Decode::decode(&mut &blob[..]).map_err(|error| {
tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to decode blob");
DispatchBlobError::InvalidEncoding
})?;
let universal_dest: InteriorLocation = universal_dest.try_into().map_err(|_| {
tracing::debug!(target: "xcm::universal_exports", "Failed to convert universal destination");
DispatchBlobError::UnsupportedLocationVersion
})?;
let intended_global = universal_dest
.global_consensus()
.map_err(|()| {
tracing::debug!(target: "xcm::universal_exports", "Failed to get global consensus from universal destination");
DispatchBlobError::NonUniversalDestination })?;
ensure!(intended_global == our_global, DispatchBlobError::WrongGlobal);
let dest = universal_dest.relative_to(&our_universal);
let mut message: Xcm<()> = message.try_into().map_err(|_| {
tracing::debug!(target: "xcm::universal_exports", "Failed to convert message");
DispatchBlobError::UnsupportedXcmVersion
})?;
if let Some(bridge_instance) = OurPlaceBridgeInstance::get() {
message.0.insert(0, DescendOrigin(bridge_instance));
}
send_xcm::<Router>(dest, message).map_err(|error| {
tracing::debug!(target: "xcm::universal_exports", ?error, "Failed to send XCM");
DispatchBlobError::RoutingError
})?;
Ok(())
}
}
pub struct HaulBlobExporter<Bridge, BridgedNetwork, DestinationVersion, Price>(
PhantomData<(Bridge, BridgedNetwork, DestinationVersion, Price)>,
);
impl<
Bridge: HaulBlob,
BridgedNetwork: Get<Location>,
DestinationVersion: GetVersion,
Price: Get<Assets>,
> ExportXcm for HaulBlobExporter<Bridge, BridgedNetwork, DestinationVersion, Price>
{
type Ticket = (Vec<u8>, XcmHash);
fn validate(
network: NetworkId,
_channel: u32,
universal_source: &mut Option<InteriorLocation>,
destination: &mut Option<InteriorLocation>,
message: &mut Option<Xcm<()>>,
) -> Result<((Vec<u8>, XcmHash), Assets), SendError> {
let (bridged_network, bridged_network_location_parents) = {
let Location { parents, interior: mut junctions } = BridgedNetwork::get();
match junctions.take_first() {
Some(GlobalConsensus(network)) => (network, parents),
_ => return Err(NotApplicable),
}
};
ensure!(&network == &bridged_network, NotApplicable);
let dest = destination.take().ok_or(SendError::MissingArgument)?;
let (universal_dest, version) =
match dest.pushed_front_with(GlobalConsensus(bridged_network)) {
Ok(d) => {
let version = DestinationVersion::get_version_for(&Location::from(
AncestorThen(bridged_network_location_parents, d.clone()),
))
.ok_or(SendError::DestinationUnsupported)?;
(d, version)
},
Err((dest, _)) => {
*destination = Some(dest);
return Err(NotApplicable);
},
};
let (local_net, local_sub) = universal_source
.take()
.ok_or(SendError::MissingArgument)?
.split_global()
.map_err(|()| {
tracing::debug!(target: "xcm::universal_exports", "Failed to split global consensus");
SendError::Unroutable
})?;
let mut message = message.take().ok_or(SendError::MissingArgument)?;
let maybe_id = match message.last() {
Some(SetTopic(t)) => Some(*t),
_ => None,
};
message.0.insert(0, UniversalOrigin(GlobalConsensus(local_net)));
if local_sub != Here {
message.0.insert(1, DescendOrigin(local_sub));
}
let message = VersionedXcm::from(message).into_version(version).map_err(|()| {
tracing::debug!(target: "xcm::universal_exports", "Failed to convert message to versioned XCM");
SendError::DestinationUnsupported
})?;
let universal_dest = VersionedInteriorLocation::from(universal_dest)
.into_version(version)
.map_err(|()| {
tracing::debug!(target: "xcm::universal_exports", "Failed to convert destination to versioned location");
SendError::DestinationUnsupported })?;
let id = maybe_id.unwrap_or_else(|| message.using_encoded(pezsp_io::hashing::blake2_256));
let blob = BridgeMessage { universal_dest, message }.encode();
Ok(((blob, id), Price::get()))
}
fn deliver((blob, id): (Vec<u8>, XcmHash)) -> Result<XcmHash, SendError> {
Bridge::haul_blob(blob)?;
Ok(id)
}
}
#[cfg(test)]
mod tests {
use super::*;
use pezframe_support::{
assert_err, assert_ok,
traits::{Contains, Equals},
};
#[test]
fn ensure_is_remote_works() {
let x = ensure_is_remote(Pezkuwi, (Parent, Kusama, Teyrchain(1000)));
assert_eq!(x, Ok((Kusama, Teyrchain(1000).into())));
let x = ensure_is_remote((Kusama, Teyrchain(1000)), (Parent, Parent, Pezkuwi));
assert_eq!(x, Ok((Pezkuwi, Here)));
let x = ensure_is_remote(Pezkuwi, Teyrchain(1000));
assert_eq!(x, Err(Teyrchain(1000).into()));
let x = ensure_is_remote(Pezkuwi, (Parent, Pezkuwi, Teyrchain(1000)));
assert_eq!(x, Err((Parent, Pezkuwi, Teyrchain(1000)).into()));
let x = ensure_is_remote((), (Parent, Pezkuwi, Teyrchain(1000)));
assert_eq!(x, Err((Parent, Pezkuwi, Teyrchain(1000)).into()));
}
pub struct OkFor<Filter>(PhantomData<Filter>);
impl<Filter: Contains<Location>> SendXcm for OkFor<Filter> {
type Ticket = ();
fn validate(
destination: &mut Option<Location>,
_message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
if let Some(d) = destination.as_ref() {
if Filter::contains(&d) {
return Ok(((), Assets::new()));
}
}
Err(NotApplicable)
}
fn deliver(_ticket: Self::Ticket) -> Result<XcmHash, SendError> {
Ok([0; 32])
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful_delivery(_: Option<Location>) {}
}
impl<Filter: Contains<(NetworkId, InteriorLocation)>> ExportXcm for OkFor<Filter> {
type Ticket = ();
fn validate(
network: NetworkId,
_: u32,
_: &mut Option<InteriorLocation>,
destination: &mut Option<InteriorLocation>,
_: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
if let Some(d) = destination.as_ref() {
if Filter::contains(&(network, d.clone())) {
return Ok(((), Assets::new()));
}
}
Err(NotApplicable)
}
fn deliver(_ticket: Self::Ticket) -> Result<XcmHash, SendError> {
Ok([1; 32])
}
}
fn ensure_validate_does_not_consume_dest_or_msg<S: SendXcm>(
dest: Location,
assert_result: impl Fn(SendResult<S::Ticket>),
) {
let mut dest_wrapper = Some(dest.clone());
let msg = Xcm::<()>::new();
let mut msg_wrapper = Some(msg.clone());
assert_result(S::validate(&mut dest_wrapper, &mut msg_wrapper));
assert_eq!(Some(dest), dest_wrapper);
assert_eq!(Some(msg), msg_wrapper);
}
#[test]
fn local_exporters_works() {
pezframe_support::parameter_types! {
pub Local: NetworkId = ByGenesis([0; 32]);
pub UniversalLocation: InteriorLocation = [GlobalConsensus(Local::get()), Teyrchain(1234)].into();
pub DifferentRemote: NetworkId = ByGenesis([22; 32]);
pub RemoteDestination: Junction = Teyrchain(9657);
pub RoutableBridgeFilter: (NetworkId, InteriorLocation) = (DifferentRemote::get(), RemoteDestination::get().into());
}
type RoutableBridgeExporter = OkFor<Equals<RoutableBridgeFilter>>;
type NotApplicableBridgeExporter = OkFor<()>;
assert_ok!(validate_export::<RoutableBridgeExporter>(
DifferentRemote::get(),
0,
UniversalLocation::get(),
RemoteDestination::get().into(),
Xcm::default()
));
assert_err!(
validate_export::<NotApplicableBridgeExporter>(
DifferentRemote::get(),
0,
UniversalLocation::get(),
RemoteDestination::get().into(),
Xcm::default()
),
NotApplicable
);
let local_dest: Location = (Parent, Teyrchain(5678)).into();
assert!(ensure_is_remote(UniversalLocation::get(), local_dest.clone()).is_err());
ensure_validate_does_not_consume_dest_or_msg::<
LocalExporter<RoutableBridgeExporter, UniversalLocation>,
>(local_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
let remote_dest: Location =
(Parent, Parent, DifferentRemote::get(), RemoteDestination::get()).into();
assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok());
ensure_validate_does_not_consume_dest_or_msg::<
LocalExporter<NotApplicableBridgeExporter, UniversalLocation>,
>(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
assert_ok!(send_xcm::<LocalExporter<RoutableBridgeExporter, UniversalLocation>>(
remote_dest,
Xcm::default()
));
}
#[test]
fn remote_exporters_works() {
pezframe_support::parameter_types! {
pub Local: NetworkId = ByGenesis([0; 32]);
pub UniversalLocation: InteriorLocation = [GlobalConsensus(Local::get()), Teyrchain(1234)].into();
pub DifferentRemote: NetworkId = ByGenesis([22; 32]);
pub RoutableBridge: Location = Location::new(1, Teyrchain(9657));
pub NotApplicableBridgeTable: Vec<NetworkExportTableItem> = vec![];
pub RoutableBridgeTable: Vec<NetworkExportTableItem> = vec![
NetworkExportTableItem::new(
DifferentRemote::get(),
None,
RoutableBridge::get(),
None
)
];
}
type RoutableBridgeSender = OkFor<Equals<RoutableBridge>>;
type NotApplicableBridgeSender = OkFor<()>;
assert_ok!(validate_send::<RoutableBridgeSender>(RoutableBridge::get(), Xcm::default()));
assert_err!(
validate_send::<NotApplicableBridgeSender>(RoutableBridge::get(), Xcm::default()),
NotApplicable
);
let local_dest: Location = (Parent, Teyrchain(5678)).into();
assert!(ensure_is_remote(UniversalLocation::get(), local_dest.clone()).is_err());
ensure_validate_does_not_consume_dest_or_msg::<
UnpaidRemoteExporter<
NetworkExportTable<RoutableBridgeTable>,
RoutableBridgeSender,
UniversalLocation,
>,
>(local_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
ensure_validate_does_not_consume_dest_or_msg::<
SovereignPaidRemoteExporter<
NetworkExportTable<RoutableBridgeTable>,
RoutableBridgeSender,
UniversalLocation,
>,
>(local_dest, |result| assert_eq!(Err(NotApplicable), result));
let remote_dest: Location = (Parent, Parent, DifferentRemote::get()).into();
assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok());
ensure_validate_does_not_consume_dest_or_msg::<
UnpaidRemoteExporter<
NetworkExportTable<NotApplicableBridgeTable>,
RoutableBridgeSender,
UniversalLocation,
>,
>(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
ensure_validate_does_not_consume_dest_or_msg::<
SovereignPaidRemoteExporter<
NetworkExportTable<NotApplicableBridgeTable>,
RoutableBridgeSender,
UniversalLocation,
>,
>(remote_dest, |result| assert_eq!(Err(NotApplicable), result));
let remote_dest: Location = (Parent, Parent, DifferentRemote::get()).into();
assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok());
ensure_validate_does_not_consume_dest_or_msg::<
UnpaidRemoteExporter<
NetworkExportTable<RoutableBridgeTable>,
NotApplicableBridgeSender,
UniversalLocation,
>,
>(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
ensure_validate_does_not_consume_dest_or_msg::<
SovereignPaidRemoteExporter<
NetworkExportTable<RoutableBridgeTable>,
NotApplicableBridgeSender,
UniversalLocation,
>,
>(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
assert_ok!(send_xcm::<
UnpaidRemoteExporter<
NetworkExportTable<RoutableBridgeTable>,
RoutableBridgeSender,
UniversalLocation,
>,
>(remote_dest.clone(), Xcm::default()));
assert_ok!(send_xcm::<
SovereignPaidRemoteExporter<
NetworkExportTable<RoutableBridgeTable>,
RoutableBridgeSender,
UniversalLocation,
>,
>(remote_dest, Xcm::default()));
}
#[test]
fn network_export_table_works() {
pezframe_support::parameter_types! {
pub NetworkA: NetworkId = ByGenesis([0; 32]);
pub Teyrchain1000InNetworkA: InteriorLocation = [Teyrchain(1000)].into();
pub Teyrchain2000InNetworkA: InteriorLocation = [Teyrchain(2000)].into();
pub NetworkB: NetworkId = ByGenesis([1; 32]);
pub BridgeToALocation: Location = Location::new(1, [Teyrchain(1234)]);
pub BridgeToBLocation: Location = Location::new(1, [Teyrchain(4321)]);
pub PaymentForNetworkAAndTeyrchain2000: Asset = (Location::parent(), 150).into();
pub BridgeTable: alloc::vec::Vec<NetworkExportTableItem> = alloc::vec![
NetworkExportTableItem::new(
NetworkA::get(),
Some(vec![Teyrchain1000InNetworkA::get()]),
BridgeToALocation::get(),
None
),
NetworkExportTableItem::new(
NetworkA::get(),
Some(vec![Teyrchain2000InNetworkA::get()]),
BridgeToALocation::get(),
Some(PaymentForNetworkAAndTeyrchain2000::get())
),
NetworkExportTableItem::new(
NetworkB::get(),
None,
BridgeToBLocation::get(),
None
)
];
}
let test_data: Vec<(NetworkId, InteriorLocation, Option<(Location, Option<Asset>)>)> = vec![
(NetworkA::get(), [Teyrchain(1000)].into(), Some((BridgeToALocation::get(), None))),
(NetworkA::get(), [Teyrchain(1000), GeneralIndex(1)].into(), None),
(
NetworkA::get(),
[Teyrchain(2000)].into(),
Some((BridgeToALocation::get(), Some(PaymentForNetworkAAndTeyrchain2000::get()))),
),
(NetworkA::get(), [Teyrchain(2000), GeneralIndex(1)].into(), None),
(NetworkA::get(), [Teyrchain(3000)].into(), None),
(NetworkB::get(), [Teyrchain(1000)].into(), Some((BridgeToBLocation::get(), None))),
(NetworkB::get(), [Teyrchain(2000)].into(), Some((BridgeToBLocation::get(), None))),
(NetworkB::get(), [Teyrchain(3000)].into(), Some((BridgeToBLocation::get(), None))),
];
for (network, remote_location, expected_result) in test_data {
assert_eq!(
NetworkExportTable::<BridgeTable>::exporter_for(
&network,
&remote_location,
&Xcm::default()
),
expected_result,
"expected_result: {:?} not matched for network: {:?} and remote_location: {:?}",
expected_result,
network,
remote_location,
)
}
}
}