Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 63 additions & 102 deletions runtime/src/configs/xcm_config/asset_matcher.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,80 @@
use crate::Balance;
use frame_support::traits::ContainsPair;
use xcm::latest::prelude::*;
use xcm_executor::traits::{Error as MatchError, MatchesFungibles};
use xcm_executor::traits::{Error as MatchError, MatchesFungible, MatchesFungibles};

/// A custom matcher that converts an incoming `Asset` from an XCM message into a local asset ID (`u32`) and amount (`Balance`).
///
/// This matcher is used by the XCM asset transactor to interpret location `Asset` objects
/// and translate them to known local assets that can be used within the chain’s runtime.
pub struct AssetMatcher;
/// This struct defines a native asset matcher for XCM transactions.
///
/// It identifies the native token based on its XCM location and fungibility,
/// determining if it matches the native asset.
pub struct NativeAssetMatcher;

impl MatchesFungibles<u32, Balance> for AssetMatcher {
impl MatchesFungible<Balance> for NativeAssetMatcher {
fn matches_fungible(asset: &Asset) -> Option<Balance> {
match asset {
// Match the native token
Asset {
id: AssetId(Location {
parents: 0,
interior: Junctions::Here,
}),
fun: Fungibility::Fungible(amount),
} => {
log::trace!(target: "xcm::matches_fungible", "AssetMatcher: Matched native token, amount: {:?}", amount);
Some(*amount)
},

// Otherwise, mismatched asset type
_ => {
log::trace!(target: "xcm::matches_fungible", "AssetMatcher: Asset not handled → asset: {:?}", asset);
None
}
}
}
}

/// This struct defines a multi-asset matcher for XCM transactions.
///
/// It identifies various fungible assets based on their XCM location and fungibility,
/// determining if they match known assets and extracting their identifiers and amounts.
pub struct MultiAssetMatcher;

impl MatchesFungibles<u32, Balance> for MultiAssetMatcher {
fn matches_fungibles(asset: &Asset) -> Result<(u32, Balance), MatchError> {
let match_result = match asset {
// XCM Inbound - Match the relay chain (parent)

// Match a local parachain (Xode)
Asset {
id:
AssetId(Location {
parents: 1,
interior: Junctions::Here,
}),
id: AssetId(Location {
parents: 0,
interior: Junctions::X2(junctions),
}),
fun: Fungibility::Fungible(amount),
} => match junctions.as_ref() {
[Junction::PalletInstance(50), Junction::GeneralIndex(asset_id)] => {
log::trace!(target: "xcm::matches_fungibles", "AssetMatcher: Matched Xode asset → asset_id: {:?}, amount: {:?}", asset_id, amount);
Ok((*asset_id as u32, *amount))
}
_ => Err(MatchError::AssetNotHandled),
},

// Match the relay chain (parent)
Asset {
id: AssetId(Location {
parents: 1,
interior: Junctions::Here,
}),
fun: Fungibility::Fungible(amount),
} => {
log::trace!(target: "xcm::matches_fungibles", "AssetMatcher: Matched Relay Chain native asset: amount = {:?}", amount);
Ok((100_000_000, *amount))
}

// XCM Inbound - Match a sibling parachain (e.g., AssetHub with ParaId 1000)
// Match a sibling parachain (e.g., AssetHub with ParaId 1000)
Asset {
id:
AssetId(Location {
parents: 1,
interior: Junctions::X3(junctions),
}),
id: AssetId(Location {
parents: 1,
interior: Junctions::X3(junctions),
}),
fun: Fungibility::Fungible(amount),
} => match junctions.as_ref() {
[Junction::Parachain(1000), Junction::PalletInstance(50), Junction::GeneralIndex(asset_id)] =>
Expand All @@ -47,22 +90,6 @@ impl MatchesFungibles<u32, Balance> for AssetMatcher {
_ => Err(MatchError::AssetNotHandled),
},

// XCM Outbound - Match a local parachain (e.g., Xode)
Asset {
id:
AssetId(Location {
parents: 0,
interior: Junctions::X2(junctions),
}),
fun: Fungibility::Fungible(amount),
} => match junctions.as_ref() {
[Junction::PalletInstance(50), Junction::GeneralIndex(asset_id)] => {
log::trace!(target: "xcm::matches_fungibles", "AssetMatcher: Matched Xode asset → asset_id: {:?}, amount: {:?}", asset_id, amount);
Ok((*asset_id as u32, *amount))
}
_ => Err(MatchError::AssetNotHandled),
},

// Otherwise, mismatched asset type
_ => {
log::trace!(target: "xcm::matches_fungibles", "AssetMatcher: Asset not handled → asset: {:?}", asset);
Expand All @@ -74,69 +101,3 @@ impl MatchesFungibles<u32, Balance> for AssetMatcher {
match_result
}
}

/// This filter determines whether a given asset and its origin location are considered "trusted reserve assets"
/// for XCM reserve operations. Only assets and origins that match the trusted patterns will be treated as reserves.
pub struct TrustedReserveAssets;

impl ContainsPair<Asset, Location> for TrustedReserveAssets {
fn contains(asset: &Asset, origin: &Location) -> bool {
match &origin {
// Match the relay chain (parent) as a trusted reserve asset.
Location {
parents: 1,
interior: Junctions::Here,
} => {
let result = matches!(
&asset.id,
AssetId(Location {
parents: 1,
interior: Junctions::Here
})
);
log::trace!(target: "xcm::contains_pair", "TrustedReserveAssets::contains - RelayChain → asset: {:?}, origin: {:?}, result: {:?}", asset, origin, result);

result
}

// Match a sibling parachain (e.g., AssetHub with ParaId 1000) as a trusted reserve asset.
Location {
parents: 1,
interior: Junctions::X1(parachain_junction),
} => match parachain_junction.as_ref() {
[Junction::Parachain(1000)] => {
if let AssetId(Location {
parents: 1,
interior: Junctions::X3(asset_junctions),
}) = &asset.id
{
let result = matches!(
asset_junctions.as_ref(),
[
Junction::Parachain(1000),
Junction::PalletInstance(50),
Junction::GeneralIndex(_)
]
);
log::trace!(target: "xcm::contains_pair", "TrustedReserveAssets::contains - AssetHub → asset: {:?}, origin: {:?}, result: {:?}", asset, origin, result);

result
} else {
log::trace!(target: "xcm::contains_pair", "TrustedReserveAssets::contains - AssetHub mismatch → asset: {:?}, origin: {:?}", asset, origin);
false
}
}
_ => {
log::trace!(target: "xcm::contains_pair", "TrustedReserveAssets::contains - Non-AssetHub sibling parachain → asset: {:?}, origin: {:?}", asset, origin);
false
}
},

// Any other origin or asset combination is not considered a trusted reserve asset and will return `false`.
_ => {
log::trace!(target: "xcm::contains_pair", "TrustedReserveAssets::contains - Other → asset: {:?}, origin: {:?}", asset, origin);
false
}
}
}
}
45 changes: 35 additions & 10 deletions runtime/src/configs/xcm_config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use crate::{
XcmpQueue,

// XCM config modules
configs::xcm_config::asset_matcher::{AssetMatcher, TrustedReserveAssets},
configs::xcm_config::asset_matcher::{NativeAssetMatcher, MultiAssetMatcher},
configs::xcm_config::trusted_reserve_assets::TrustedReserveAssets,
configs::xcm_config::origin_filters::ParentOrTrustedSiblings,
configs::xcm_config::weight_trader::DynamicWeightTrader,
};
Expand All @@ -29,7 +30,7 @@ use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin,
FixedWeightBounds, FrameTransactionalProcessor, FungiblesAdapter, LocalMint, ParentIsPreset,
FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, LocalMint, ParentIsPreset,
RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
TrailingSetTopicAsId, WithComputedOrigin, WithUniqueTopic,
Expand Down Expand Up @@ -60,15 +61,32 @@ pub type LocationToAccountId = (
AccountId32Aliases<RelayNetwork, AccountId>,
);

/// The `AssetTransactor` defines how the runtime handles fungible assets received or sent via XCM.
/// It interprets incoming `Asset` locations, resolves them to local asset IDs and balances,
/// and executes operations such as minting, burning, or transferring tokens.
pub type AssetTransactor = FungiblesAdapter<
// The asset handler used to inspect, mint, and burn tokens (e.g., orml-tokens or pallet-assets).
/// The asset transactor for handling the local native asset.
///
/// This supports only the native token of this parachain.
/// It uses the `Balances` pallet to manage the native currency.
pub type LocalAssetTransactor = FungibleAdapter<
// The asset handler for the native currency (Balances pallet).
Balances,
// Our custom asset matcher for the native token.
NativeAssetMatcher,
// Resolves `Location` origin accounts into native `AccountId`s.
LocationToAccountId,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
// We don't track any teleports.
(),
>;

/// The asset transactor for handling assets via pallet-assets.
///
/// This supports assets from the Relay Chain, sibling parachains (e.g., AssetHub),
/// and local pallet-assets defined on this parachain.
pub type PalletAssetsTransactor = FungiblesAdapter<
// The asset handler used to inspect, mint, and burn tokens (pallet-assets).
Assets,
// Custom matcher for converting incoming `Asset` to local (asset ID, balance) pairs.
// This supports Relay Chain, sibling parachains, or AssetHub assets.
AssetMatcher,
// Our custom asset matcher for various fungible assets.
MultiAssetMatcher,
// Resolves `Location` origin accounts into native `AccountId`s.
LocationToAccountId,
// Native account identifier type used by the runtime.
Expand All @@ -81,6 +99,13 @@ pub type AssetTransactor = FungiblesAdapter<
CheckingAccount,
>;

/// The overall asset transactor for XCM, combining local native asset handling
/// and pallet-assets handling for other fungible assets.
pub type AssetTransactor = (
LocalAssetTransactor,
PalletAssetsTransactor
);

/// This is the type we use to convert an (incoming) XCM origin into a local Origin instance,
/// ready for dispatching a transaction with Xcm's Transact. There is an OriginKind which can
/// biases the kind of local Origin it will become.
Expand Down
1 change: 1 addition & 0 deletions runtime/src/configs/xcm_config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod asset_matcher;
mod trusted_reserve_assets;
mod origin_filters;
pub mod weight_trader;
mod config;
Expand Down
105 changes: 105 additions & 0 deletions runtime/src/configs/xcm_config/trusted_reserve_assets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use frame_support::traits::ContainsPair;
use xcm::latest::prelude::*;

/// This struct defines trusted reserve assets for XCM transactions.
///
/// It identifies assets based on their XCM location and the origin of the XCM message,
/// determining if they are considered trusted reserve assets.
pub struct TrustedReserveAssets;

impl ContainsPair<Asset, Location> for TrustedReserveAssets {
fn contains(asset: &Asset, origin: &Location) -> bool {
log::trace!(target: "xcm::contains_pair", "TrustedReserveAssets::contains - asset: {:?}, origin: {:?}", asset, origin);

let result = match &origin {

// Match the relay chain (parent) origin.
Location {
parents: 1,
interior: Junctions::Here,
} if matches!(
&asset.id,
AssetId(Location {
parents: 1,
interior: Junctions::Here
})
) => true,

// Match a sibling parachain origin (e.g., AssetHub with ParaId 1000).
Location {
parents: 1,
interior: Junctions::X1(parachain_junction),
} => match parachain_junction.as_ref() {

// Match AssetHub (parachain 1000) origin.
[Junction::Parachain(1000)] => match &asset.id {

// Outbound: XCM assets (pallet-assets) from Xode to AssetHub.
AssetId(Location {
parents: 0,
interior: Junctions::X2(asset_junctions),
}) if matches!(
asset_junctions.as_ref(),
[Junction::PalletInstance(50), Junction::GeneralIndex(_)]
) => true,

// Inbound: XCM assets (pallet-assets) from AssetHub to Xode.
AssetId(Location {
parents: 1,
interior: Junctions::X3(asset_junctions),
}) if matches!(
asset_junctions.as_ref(),
[
Junction::Parachain(1000),
Junction::PalletInstance(50),
Junction::GeneralIndex(_)
]
) => true,

_ => false
},

// Match Hydration (parachain 2034) origin.
[Junction::Parachain(2034)] => match &asset.id {

// Outbound: XCM assets (native XON) from Xode to Hydration.
AssetId(Location {
parents: 0,
interior: Junctions::Here,
}) => true,

// Outbound: XCM assets (pallet-assets) from Xode to Hydration.
AssetId(Location {
parents: 0,
interior: Junctions::X2(asset_junctions),
}) if matches!(
asset_junctions.as_ref(),
[Junction::PalletInstance(50), Junction::GeneralIndex(_)]
) => true,

// Inbound: XCM assets (pallet-assets) from Hydration to Xode.
AssetId(Location {
parents: 1,
interior: Junctions::X3(asset_junctions),
}) if matches!(
asset_junctions.as_ref(),
[
Junction::Parachain(2034),
Junction::PalletInstance(51),
Junction::GeneralIndex(_)
]
) => true,

_ => false
},

_ => false
},

_ => false
};

log::trace!(target: "xcm::contains_pair", "TrustedReserveAssets::contains - asset: {:?}, origin: {:?} → result: {:?}", asset, origin, result);
result
}
}
Loading
Loading