diff --git a/runtime/src/configs/xcm_config/asset_matcher.rs b/runtime/src/configs/xcm_config/asset_matcher.rs index 74671894..84bce3e5 100644 --- a/runtime/src/configs/xcm_config/asset_matcher.rs +++ b/runtime/src/configs/xcm_config/asset_matcher.rs @@ -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 for AssetMatcher { +impl MatchesFungible for NativeAssetMatcher { + fn matches_fungible(asset: &Asset) -> Option { + 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 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)] => @@ -47,22 +90,6 @@ impl MatchesFungibles 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); @@ -74,69 +101,3 @@ impl MatchesFungibles 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 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 - } - } - } -} diff --git a/runtime/src/configs/xcm_config/config.rs b/runtime/src/configs/xcm_config/config.rs index 172e74df..737c4ec9 100644 --- a/runtime/src/configs/xcm_config/config.rs +++ b/runtime/src/configs/xcm_config/config.rs @@ -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, }; @@ -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, @@ -60,15 +61,32 @@ pub type LocationToAccountId = ( AccountId32Aliases, ); -/// 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. @@ -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. diff --git a/runtime/src/configs/xcm_config/mod.rs b/runtime/src/configs/xcm_config/mod.rs index 9b0e306a..40b05b11 100644 --- a/runtime/src/configs/xcm_config/mod.rs +++ b/runtime/src/configs/xcm_config/mod.rs @@ -1,4 +1,5 @@ mod asset_matcher; +mod trusted_reserve_assets; mod origin_filters; pub mod weight_trader; mod config; diff --git a/runtime/src/configs/xcm_config/trusted_reserve_assets.rs b/runtime/src/configs/xcm_config/trusted_reserve_assets.rs new file mode 100644 index 00000000..b93a3671 --- /dev/null +++ b/runtime/src/configs/xcm_config/trusted_reserve_assets.rs @@ -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 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 + } +} diff --git a/runtime/src/configs/xcm_config/weight_trader.rs b/runtime/src/configs/xcm_config/weight_trader.rs index 65fe14ba..113a0a60 100644 --- a/runtime/src/configs/xcm_config/weight_trader.rs +++ b/runtime/src/configs/xcm_config/weight_trader.rs @@ -1,4 +1,4 @@ -use crate::{WeightToFee, configs::xcm_config::AssetTransactor}; +use crate::{configs::xcm_config::AssetTransactor}; use frame_support::{ parameter_types, weights::{Weight, WeightToFee as WeightToFeeT}, @@ -10,47 +10,68 @@ use xcm_executor::{ }; use alloc::sync::Arc; use sp_core::crypto::{Ss58Codec, AccountId32}; +use core::marker::PhantomData; parameter_types! { pub const RelayLocation: Location = Location::parent(); } -/// A weight to fee implementation for USDT, which is used to convert weight into a fee -/// that can be paid in USDT. This implementation is specifically designed to handle -/// the conversion of weight into a fee amount that can be used for weight purchasing -/// in the context of XCM transactions. +/// Trait defining parameters for weight-to-fee conversion for different assets. /// -/// The fee is calculated based on the weight's reference time, divided by a scaling factor -/// to convert it into a fee amount in USDT. -/// -/// The scaling factor is set to 1,000,000 to ensure that the fee is reasonable and -/// can be handled by the USDT asset. -/// -/// This implementation is useful for scenarios where USDT is used as the asset for weight purchasing, -/// allowing for dynamic handling of weight purchasing based on the available assets in the `AssetsInHolding`. -pub struct UsdtWeightToFee; +/// Implementers of this trait specify the fee rate per second for a given asset, +/// allowing the `WeightToFeeConverter` to calculate the appropriate fee based on +/// the weight of the XCM execution. +pub trait WeightToFeeAssetParams { + const FEE_PER_SECOND: u128; +} + +/// Weight-to-fee parameters for XON (native token) with a rate of 0.01 XON per second. +pub struct XonWeightToFeeRate; +impl WeightToFeeAssetParams for XonWeightToFeeRate { + const FEE_PER_SECOND: u128 = 10_000_000_000; +} + +/// Weight-to-fee parameters for DOT (relay chain token) with a rate of 0.01 DOT per second. +pub struct DotWeightToFeeRate; +impl WeightToFeeAssetParams for DotWeightToFeeRate { + const FEE_PER_SECOND: u128 = 100_000_000; // 0.01 DOT +} -impl WeightToFeeT for UsdtWeightToFee { +/// Weight-to-fee parameters for USDT (AssetHub token) with a rate of 0.01 USDT per second. +pub struct UsdtWeightToFeeRate; +impl WeightToFeeAssetParams for UsdtWeightToFeeRate { + const FEE_PER_SECOND: u128 = 10_000; // 0.01 USDT +} + +/// A generic weight-to-fee converter that calculates the fee based on the weight +/// of the XCM execution and the fee rate defined by the implementer of +/// `WeightToFeeAssetParams`. +/// +/// This struct uses the `WeightToFeeAssetParams` trait to determine the fee rate +/// for the specific asset, allowing for flexible fee calculations based on the +/// asset being used for payment. +pub struct WeightToFeeConverter(PhantomData); + +impl WeightToFeeT for WeightToFeeConverter { type Balance = u128; fn weight_to_fee(weight: &Weight) -> Self::Balance { - weight.ref_time().saturating_div(1_000_000).max(1).into() + let picos_per_second: u64 = 1_000_000_000_000u64; + let ref_time_picoseconds = weight.ref_time(); + let fee = ref_time_picoseconds.saturating_mul(T::FEE_PER_SECOND as u64); + + (fee.saturating_div(picos_per_second)) as u128 } } -/// A dynamic weight trader that determines how to buy weight for XCM execution -/// based on the assets provided in the `AssetsInHolding`. This trader supports -/// both DOT from the Relay Chain and USDT from AssetHub (parachain 1000). -/// -/// It matches assets by their XCM location and uses the corresponding weight-to-fee -/// conversion logic to calculate the cost for the requested weight. -/// -/// If the asset originates from the Relay Chain, the trader uses DOT for payment. -/// If it originates from AssetHub, it uses USDT. The function processes the payment, -/// deducts the total fee, and returns any remaining balance to the caller. -/// -/// If no supported asset is detected, the trader returns an error indicating that -/// the weight purchase cannot be completed. +/// Dynamic weight trader that calculates fees based on the asset used for payment. +/// +/// It supports XON (native token), DOT (relay chain token), and USDT (AssetHub token). +/// The fee is calculated based on the weight of the XCM execution and a fixed fee +/// of 0.01 units of the respective asset. +/// +/// This implementation deducts the required fee from the provided assets and deposits +/// it to a predefined fee collector account. pub struct DynamicWeightTrader; impl WeightTrader for DynamicWeightTrader { @@ -71,10 +92,15 @@ impl WeightTrader for DynamicWeightTrader { // Check if asset matches USDT on AssetHub (parachain 1000) match asset_id { + AssetId(xon_location @ Location { + parents: 0, + interior: Junctions::Here, + }) => handle_payment(xon_location.clone(), payment, WeightToFeeConverter::::weight_to_fee(&weight)), + AssetId(dot_location @ Location { parents: 1, interior: Junctions::Here, - }) => handle_payment(dot_location.clone(), payment, WeightToFee::weight_to_fee(&weight)), + }) => handle_payment(dot_location.clone(), payment, WeightToFeeConverter::::weight_to_fee(&weight)), AssetId(usdt_location @ Location { parents: 1, @@ -86,7 +112,7 @@ impl WeightTrader for DynamicWeightTrader { Junction::PalletInstance(50), Junction::GeneralIndex(1984) ] - ) => handle_payment(usdt_location.clone(), payment, UsdtWeightToFee::weight_to_fee(&weight)), + ) => handle_payment(usdt_location.clone(), payment, WeightToFeeConverter::::weight_to_fee(&weight)), _ => { log::trace!(target: "xcm::weight_trader", "DynamicWeightTrader::buy_weight - Unsupported asset: {:?}", asset_id); @@ -100,24 +126,25 @@ impl WeightTrader for DynamicWeightTrader { } } -/// Handles a payment for purchasing XCM execution weight, supporting both DOT and USDT. -/// The function validates the provided asset balance and ensures it can fully cover the total cost. -/// The total fee consists of the required XCM execution fee plus a fixed surcharge of 0.01 (DOT or USDT). -/// -/// If the asset originates from the Relay Chain, the payment is processed using DOT. -/// If the asset comes from AssetHub (parachain 1000), it is processed using USDT. -/// The combined fee amount is then transferred to a predefined receiver account, -/// which is converted into an XCM-compatible location for proper asset deposit handling. -/// -/// After deducting the total fee (execution fee + 0.01), the function returns any remaining -/// balance to the caller. Detailed logging throughout the process ensures transparency -/// and simplifies debugging and auditing. +/// Handles the payment for weight purchase by deducting the required fee +/// from the provided assets and depositing it to the fee collector. +/// +/// The function calculates the total fee based on the provided `fee_amount` +/// and a fixed fee of 0.01 units of the asset. It then deducts this total fee +/// from the `payment` assets. If successful, it deposits the fee to a predefined +/// fee collector account and returns any remaining assets. fn handle_payment( asset_location: Location, payment: AssetsInHolding, fee_amount: u128, ) -> Result { let fixed_fee: u128 = match asset_location.clone() { + // XON: local chain (12 decimals -> 0.01 XON = 10_000_000_000) + Location { + parents: 0, + interior: Junctions::Here, + } => 10_000_000_000, + // DOT: relay chain (10 decimals -> 0.01 DOT = 100_000_000) Location { parents: 1, diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 14c9f9e8..6a6b7c6a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -62,7 +62,7 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, weights::{ constants::WEIGHT_REF_TIME_PER_SECOND, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, - WeightToFeePolynomial, + WeightToFeePolynomial, WeightToFee as WeightToFeeConversion, }, }; @@ -77,20 +77,18 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use configs::{ RuntimeBlockWeights, xcm_config::{ - RelayLocation, XcmConfig, XcmRouter, LocationToAccountId, - weight_trader::DynamicWeightTrader + XcmConfig, XcmRouter, LocationToAccountId, + weight_trader::{WeightToFeeConverter, XonWeightToFeeRate, DotWeightToFeeRate, UsdtWeightToFeeRate} } }; use xcm::{ latest::prelude::{ - Asset, AssetId, Junctions, Junction, Location, - XcmContext, XcmHash, + AssetId, Junctions, Junction, Location, }, Version as XcmVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, }; -use xcm_executor::{traits::WeightTrader, AssetsInHolding}; use xcm_runtime_apis::{ dry_run::{ CallDryRunEffects as ApiCallDryRunEffects, @@ -754,7 +752,20 @@ impl_runtime_apis! { impl xcm_runtime_apis::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { let mut acceptable_assets: Vec = Vec::new(); - acceptable_assets.push(AssetId(RelayLocation::get())); + + // XON: Xode native token + acceptable_assets.push(AssetId(Location { + parents: 0, + interior: Junctions::Here, + })); + + // DOT: Relay chain native token + acceptable_assets.push(AssetId(Location { + parents: 1, + interior: Junctions::Here, + })); + + // USDT: AssetHub parachain asset (1984) acceptable_assets.push(AssetId(Location { parents: 1, interior: Junctions::X3(Arc::from([ @@ -773,37 +784,44 @@ impl_runtime_apis! { .map_err(|_| XcmPaymentApiError::WeightNotComputable) } - // This implementation is based on the approach taken in the latest version of pallet-xcm. - // Reference: https://docs.rs/pallet-xcm/22.0.1/src/pallet_xcm/lib.rs.html#3223 - // - // The version of pallet-xcm currently used in this chain is outdated and does not yet - // provide this functionality. To bridge the gap, we implemented `query_weight_to_asset_fee` - // in our runtime by following the design from `xcm-runtime-apis` and aligning it with the - // approach used upstream. - // - // Once the chain upgrades to a newer release of pallet-xcm, this implementation can be - // revisited to determine whether it should be replaced with the upstream version. fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let asset: AssetId = asset.clone().try_into() - .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; - - let max_amount = u128::MAX / 2; - let max_payment: Asset = (asset.clone(), max_amount).into(); - let payment = AssetsInHolding::from(max_payment); - let context = XcmContext::with_message_id(XcmHash::default()); - - let mut trader = DynamicWeightTrader::new(); - let unspent_assets = trader - .buy_weight(weight, payment, &context) - .map_err(|_| XcmPaymentApiError::WeightNotComputable)?; - - let Some(unspent) = unspent_assets.fungible.get(&asset) else { - return Err(XcmPaymentApiError::AssetNotFound); - }; - - let paid = max_amount - unspent; - - Ok(paid) + // Convert VersionedAssetId to AssetId + let asset: AssetId = asset.clone() + .try_into() + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + // Add the fixed fee (0.01 DOT or USDT, depending on asset) + let total_fee: u128 = match asset { + // XON: local chain (12 decimals -> 0.01 XON = 10_000_000_000) + AssetId(Location { + parents: 0, + interior: Junctions::Here, + }) => WeightToFeeConverter::::weight_to_fee(&weight).saturating_add(10_000_000_000u128), + + // DOT: Relay chain (10 decimals -> 0.01 DOT = 100_000_000) + AssetId(Location { + parents: 1, + interior: Junctions::Here, + }) => WeightToFeeConverter::::weight_to_fee(&weight).saturating_add(100_000_000u128), + + // USDT: AssetHub parachain asset (1984) (6 decimals -> 0.01 USDT = 10_000) + AssetId(Location { + parents: 1, + interior: Junctions::X3(ref junctions), + }) if matches!( + junctions.as_ref(), + [ + Junction::Parachain(1000), + Junction::PalletInstance(50), + Junction::GeneralIndex(1984) + ] + ) => WeightToFeeConverter::::weight_to_fee(&weight).saturating_add(10_000u128), + + _ => return Err(XcmPaymentApiError::AssetNotFound), + }; + + // Ensure the fee does not exceed the maximum payment + Ok(total_fee) } fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { diff --git a/zombienet/zombienet-local.toml b/zombienet/zombienet-local.toml index 416f79a2..41cdfaf7 100644 --- a/zombienet/zombienet-local.toml +++ b/zombienet/zombienet-local.toml @@ -23,18 +23,28 @@ chain = "rococo-local" name = "dave" validator = true -# [[parachains]] -# id = 1000 -# cumulus_based = true +[[parachains]] +id = 1000 +cumulus_based = true + + [[parachains.collators]] + name = "dummy-assethub-collator" + command = "../target/release/xode-node" + args = ["-lxcmp=trace", "-lxcm=trace", "-lruntime=debug"] + ws_port = 9977 -# [[parachains.collators]] -# name = "assethub-collator" -# command = "./bin/polkadot-parachain" -# args = ["-lxcmp=trace", "-lxcm=trace", "-lruntime=debug"] -# ws_port = 9988 +[[parachains]] +id = 2034 +cumulus_based = true + + [[parachains.collators]] + name = "dummy-parachain-collator" + command = "../target/release/xode-node" + args = ["-lxcmp=trace", "-lxcm=trace", "-lruntime=debug"] + ws_port = 9988 [[parachains]] -id = 4607 +id = 3417 cumulus_based = true [[parachains.collators]]