Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
32 changes: 5 additions & 27 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1692,33 +1692,11 @@ impl_runtime_apis! {
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
let native_asset = xcm_config::WestendLocation::get();
let fee_in_native = WeightToFee::weight_to_fee(&weight);
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
match latest_asset_id {
Ok(asset_id) if asset_id.0 == native_asset => {
// for native asset
Ok(fee_in_native)
},
Ok(asset_id) => {
// Try to get current price of `asset_id` in `native_asset`.
if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::<Runtime>::quote_price_tokens_for_exact_tokens(
asset_id.0.clone(),
native_asset,
fee_in_native,
true, // We include the fee.
) {
Ok(swapped_in_native)
} else {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
Err(XcmPaymentApiError::AssetNotFound)
}
},
Err(_) => {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!");
Err(XcmPaymentApiError::VersionedConversionFailed)
}
}
use crate::xcm_config::XcmConfig;

type Trader = <XcmConfig as xcm_executor::Config>::Trader;

PolkadotXcm::query_weight_to_asset_fee::<Trader>(weight, asset)
}

fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
Expand Down
55 changes: 55 additions & 0 deletions polkadot/xcm/pallet-xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use sp_runtime::{
},
Either, RuntimeDebug, SaturatedConversion,
};
use storage::{with_transaction, TransactionOutcome};
use xcm::{latest::QueryResponseInfo, prelude::*};
use xcm_builder::{
ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController,
Expand Down Expand Up @@ -2954,6 +2955,60 @@ impl<T: Config> Pallet<T> {
})
}

/// Computes the weight cost using the provided `WeightTrader`.
///
/// The provided `WeightTrader` must be the same as the one used in the XcmExecutor to ensure
/// uniformity in the weight cost calculation.
///
/// NOTE: Currently this function uses a workaround that should be good enough for all practical
/// uses: passes `u128::MAX / 2 == 2^127` of the specified asset to the `WeightTrader` as
/// payment and computes the weight cost as the difference between this and the unspent amount.
///
/// Some weight traders could add the provided payment to some account's balance. However,
/// it should practically never result in overflow because even currencies with a lot of decimal
/// digits (say 18) usually have the total issuance of billions (`x * 10^9`) or trillions (`x *
/// 10^12`) at max, much less than `2^127 / 10^18 =~ 1.7 * 10^20` (170 billion billion). Thus,
/// any account's balance most likely holds less than `2^127`, so adding `2^127` won't result in
/// `u128` overflow.
pub fn query_weight_to_asset_fee<Trader: xcm_executor::traits::WeightTrader>(
weight: Weight,
asset: VersionedAssetId,
) -> Result<u128, XcmPaymentApiError> {
let asset: AssetId = asset.clone().try_into()
.map_err(|e| {
tracing::error!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset, "Failed to convert versioned asset");
XcmPaymentApiError::VersionedConversionFailed
})?;

let max_amount = u128::MAX / 2;
let max_payment: Asset = (asset.clone(), max_amount).into();
let context = XcmContext::with_message_id(XcmHash::default());

let mut trader = Trader::new();

// We return the unspent amount without affecting the state
// as we used a big amount of the asset without any check.
let unspent = with_transaction(|| {
let result = trader.buy_weight(weight, max_payment.into(), &context)
.map_err(|e| {
tracing::error!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset, "Failed to buy weight");

// Return something convertible to `DispatchError` as required by the `with_transaction` fn.
DispatchError::Other("Failed to buy weight")
});

TransactionOutcome::Rollback(result)
}).map_err(|_| XcmPaymentApiError::AssetNotFound)?;

let Some(unspent) = unspent.fungible.get(&asset) else {
tracing::error!(target: "xcm::pallet::query_weight_to_asset_fee", ?asset, "The trader didn't return the needed fungible asset");
return Err(XcmPaymentApiError::AssetNotFound);
};

let paid = max_amount - unspent;
Ok(paid)
}

/// Given a `destination` and XCM `message`, return assets to be charged as XCM delivery fees.
pub fn query_delivery_fees(
destination: VersionedLocation,
Expand Down
Loading