Skip to content

Commit cf8b5f2

Browse files
Assets in pool with native can be used in query_weight_to_asset_fee (#6080)
A follow-up to #5599. Assets in a pool with the native one are returned from `query_acceptable_payment_assets`. Now those assets can be used in `query_weight_to_asset_fee` to get the correct amount that needs to be paid. --------- Co-authored-by: command-bot <> (cherry picked from commit b4732ad)
1 parent fc62793 commit cf8b5f2

9 files changed

Lines changed: 201 additions & 13 deletions

File tree

cumulus/parachains/integration-tests/emulated/common/src/macros.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,3 +450,71 @@ macro_rules! test_dry_run_transfer_across_pk_bridge {
450450
}
451451
};
452452
}
453+
454+
#[macro_export]
455+
macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub {
456+
( $asset_hub:ty ) => {
457+
$crate::macros::paste::paste! {
458+
use emulated_integration_tests_common::USDT_ID;
459+
use xcm_runtime_apis::fees::{Error as XcmPaymentApiError, runtime_decl_for_xcm_payment_api::XcmPaymentApiV1};
460+
461+
$asset_hub::execute_with(|| {
462+
// Setup a pool between USDT and WND.
463+
type RuntimeOrigin = <$asset_hub as Chain>::RuntimeOrigin;
464+
type Assets = <$asset_hub as [<$asset_hub Pallet>]>::Assets;
465+
type AssetConversion = <$asset_hub as [<$asset_hub Pallet>]>::AssetConversion;
466+
let wnd = Location::new(1, []);
467+
let usdt = Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
468+
let sender = [<$asset_hub Sender>]::get();
469+
assert_ok!(AssetConversion::create_pool(
470+
RuntimeOrigin::signed(sender.clone()),
471+
Box::new(wnd.clone()),
472+
Box::new(usdt.clone()),
473+
));
474+
475+
type Runtime = <$asset_hub as Chain>::Runtime;
476+
let acceptable_payment_assets = Runtime::query_acceptable_payment_assets(4).unwrap();
477+
assert_eq!(acceptable_payment_assets, vec![
478+
VersionedAssetId::from(AssetId(wnd.clone())),
479+
VersionedAssetId::from(AssetId(usdt.clone())),
480+
]);
481+
482+
let program = Xcm::<()>::builder()
483+
.withdraw_asset((Parent, 100u128))
484+
.buy_execution((Parent, 10u128), Unlimited)
485+
.deposit_asset(All, [0u8; 32])
486+
.build();
487+
let weight = Runtime::query_xcm_weight(VersionedXcm::from(program)).unwrap();
488+
let fee_in_wnd = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(wnd.clone()))).unwrap();
489+
// Assets not in a pool don't work.
490+
assert!(Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1)])))).is_err());
491+
let fee_in_usdt_fail = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt.clone())));
492+
// Weight to asset fee fails because there's not enough asset in the pool.
493+
// We just created it, there's none.
494+
assert_eq!(fee_in_usdt_fail, Err(XcmPaymentApiError::AssetNotFound));
495+
// We add some.
496+
assert_ok!(Assets::mint(
497+
RuntimeOrigin::signed(sender.clone()),
498+
USDT_ID.into(),
499+
sender.clone().into(),
500+
5_000_000_000_000
501+
));
502+
// We make 1 WND = 4 USDT.
503+
assert_ok!(AssetConversion::add_liquidity(
504+
RuntimeOrigin::signed(sender.clone()),
505+
Box::new(wnd),
506+
Box::new(usdt.clone()),
507+
1_000_000_000_000,
508+
4_000_000_000_000,
509+
0,
510+
0,
511+
sender.into()
512+
));
513+
// Now it works.
514+
let fee_in_usdt = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt)));
515+
assert_ok!(fee_in_usdt);
516+
assert!(fee_in_usdt.unwrap() > fee_in_wnd);
517+
});
518+
}
519+
};
520+
}

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ mod imports {
3535
// Cumulus
3636
pub use asset_test_utils::xcm_helpers;
3737
pub use emulated_integration_tests_common::{
38-
test_parachain_is_trusted_teleporter,
38+
test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay,
39+
test_relay_is_trusted_teleporter, test_xcm_fee_querying_apis_work_for_asset_hub,
3940
xcm_emulator::{
4041
assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test,
4142
TestArgs, TestContext, TestExt,

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() {
389389
);
390390
});
391391
}
392+
393+
#[test]
394+
fn xcm_fee_querying_apis_work() {
395+
test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubRococo);
396+
}

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ mod imports {
3535
// Cumulus
3636
pub use asset_test_utils::xcm_helpers;
3737
pub use emulated_integration_tests_common::{
38-
test_parachain_is_trusted_teleporter,
38+
test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay,
39+
test_relay_is_trusted_teleporter, test_xcm_fee_querying_apis_work_for_asset_hub,
3940
xcm_emulator::{
4041
assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test,
4142
TestArgs, TestContext, TestExt,

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() {
389389
);
390390
});
391391
}
392+
393+
#[test]
394+
fn xcm_fee_querying_apis_work() {
395+
test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubWestend);
396+
}

cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,19 +1326,45 @@ impl_runtime_apis! {
13261326

13271327
impl xcm_runtime_apis::fees::XcmPaymentApi<Block> for Runtime {
13281328
fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
1329-
let acceptable_assets = vec![AssetId(xcm_config::TokenLocation::get())];
1329+
let native_token = xcm_config::TokenLocation::get();
1330+
// We accept the native token to pay fees.
1331+
let mut acceptable_assets = vec![AssetId(native_token.clone())];
1332+
// We also accept all assets in a pool with the native token.
1333+
let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::<
1334+
Runtime,
1335+
xcm::v4::Location
1336+
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
1337+
acceptable_assets.extend(assets_in_pool_with_native);
13301338
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
13311339
}
13321340

13331341
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
1342+
let native_asset = xcm_config::TokenLocation::get();
1343+
let fee_in_native = WeightToFee::weight_to_fee(&weight);
13341344
match asset.try_as::<AssetId>() {
1335-
Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => {
1345+
Ok(asset_id) if asset_id.0 == native_asset => {
13361346
// for native token
1337-
Ok(WeightToFee::weight_to_fee(&weight))
1347+
Ok(fee_in_native)
13381348
},
13391349
Ok(asset_id) => {
1340-
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
1341-
Err(XcmPaymentApiError::AssetNotFound)
1350+
let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::<
1351+
Runtime,
1352+
xcm::v4::Location
1353+
>(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?;
1354+
if assets_in_pool_with_this_asset
1355+
.into_iter()
1356+
.map(|asset_id| asset_id.0)
1357+
.any(|location| location == native_asset) {
1358+
pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
1359+
asset_id.clone().0,
1360+
native_asset,
1361+
fee_in_native,
1362+
true, // We include the fee.
1363+
).ok_or(XcmPaymentApiError::AssetNotFound)
1364+
} else {
1365+
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
1366+
Err(XcmPaymentApiError::AssetNotFound)
1367+
}
13421368
},
13431369
Err(_) => {
13441370
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!");

cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,19 +1354,46 @@ impl_runtime_apis! {
13541354

13551355
impl xcm_runtime_apis::fees::XcmPaymentApi<Block> for Runtime {
13561356
fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
1357-
let acceptable_assets = vec![AssetId(xcm_config::WestendLocation::get())];
1357+
let native_token = xcm_config::WestendLocation::get();
1358+
// We accept the native token to pay fees.
1359+
let mut acceptable_assets = vec![AssetId(native_token.clone())];
1360+
// We also accept all assets in a pool with the native token.
1361+
let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::<
1362+
Runtime,
1363+
xcm::v4::Location
1364+
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
1365+
acceptable_assets.extend(assets_in_pool_with_native);
13581366
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
13591367
}
13601368

13611369
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
1370+
let native_asset = xcm_config::WestendLocation::get();
1371+
let fee_in_native = WeightToFee::weight_to_fee(&weight);
13621372
match asset.try_as::<AssetId>() {
1363-
Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => {
1364-
// for native token
1365-
Ok(WeightToFee::weight_to_fee(&weight))
1373+
Ok(asset_id) if asset_id.0 == native_asset => {
1374+
// for native asset
1375+
Ok(fee_in_native)
13661376
},
13671377
Ok(asset_id) => {
1368-
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
1369-
Err(XcmPaymentApiError::AssetNotFound)
1378+
// We recognize assets in a pool with the native one.
1379+
let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::<
1380+
Runtime,
1381+
xcm::v4::Location
1382+
>(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?;
1383+
if assets_in_pool_with_this_asset
1384+
.into_iter()
1385+
.map(|asset_id| asset_id.0)
1386+
.any(|location| location == native_asset) {
1387+
pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
1388+
asset_id.clone().0,
1389+
native_asset,
1390+
fee_in_native,
1391+
true, // We include the fee.
1392+
).ok_or(XcmPaymentApiError::AssetNotFound)
1393+
} else {
1394+
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
1395+
Err(XcmPaymentApiError::AssetNotFound)
1396+
}
13701397
},
13711398
Err(_) => {
13721399
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!");

cumulus/parachains/runtimes/assets/common/src/lib.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ pub mod runtime_api;
2626
extern crate alloc;
2727

2828
use crate::matching::{LocalLocationPattern, ParentLocation};
29+
use alloc::vec::Vec;
30+
use codec::{Decode, EncodeLike};
31+
use core::cmp::PartialEq;
2932
use frame_support::traits::{Equals, EverythingBut};
3033
use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId};
3134
use sp_runtime::traits::TryConvertInto;
@@ -134,6 +137,36 @@ pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> =
134137
TryConvertInto,
135138
>;
136139

140+
/// Returns an iterator of all assets in a pool with `asset`.
141+
///
142+
/// Should only be used in runtime APIs since it iterates over the whole
143+
/// `pallet_asset_conversion::Pools` map.
144+
///
145+
/// It takes in any version of an XCM Location but always returns the latest one.
146+
/// This is to allow some margin of migrating the pools when updating the XCM version.
147+
///
148+
/// An error of type `()` is returned if the version conversion fails for XCM locations.
149+
/// This error should be mapped by the caller to a more descriptive one.
150+
pub fn get_assets_in_pool_with<
151+
Runtime: pallet_asset_conversion::Config<PoolId = (L, L)>,
152+
L: TryInto<Location> + Clone + Decode + EncodeLike + PartialEq,
153+
>(
154+
asset: &L,
155+
) -> Result<Vec<AssetId>, ()> {
156+
pallet_asset_conversion::Pools::<Runtime>::iter_keys()
157+
.filter_map(|(asset_1, asset_2)| {
158+
if asset_1 == *asset {
159+
Some(asset_2)
160+
} else if asset_2 == *asset {
161+
Some(asset_1)
162+
} else {
163+
None
164+
}
165+
})
166+
.map(|location| location.try_into().map_err(|_| ()).map(AssetId))
167+
.collect::<Result<Vec<_>, _>>()
168+
}
169+
137170
#[cfg(test)]
138171
mod tests {
139172
use super::*;

prdoc/pr_6080.prdoc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
2+
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
3+
4+
title: Assets in pool with native can be used in query_weight_to_asset_fee in Asset Hubs
5+
6+
doc:
7+
- audience: Runtime User
8+
description: |
9+
`query_weight_to_asset_fee` now works with assets in a pool with the native asset in both
10+
Westend and Rococo asset hubs.
11+
This means all the information you get from `query_acceptable_payment_assets` can be used
12+
directly in `query_weight_to_asset_fee` to get the correct fees that need to be paid.
13+
14+
crates:
15+
- name: assets-common
16+
bump: minor
17+
- name: asset-hub-westend-runtime
18+
bump: minor
19+
- name: asset-hub-rococo-runtime
20+
bump: minor
21+
- name: emulated-integration-tests-common
22+
bump: minor

0 commit comments

Comments
 (0)