Skip to content

Commit 139691b

Browse files
franciscoaguirrebkonturactions-user
authored
Fix XcmPaymentApi::query_weight_to_asset_fee version conversion (#6459)
The `query_weight_to_asset_fee` function was trying to convert versions by using `try_as`, this function [doesn't convert from a versioned to a concrete type](https://github.com/paritytech/polkadot-sdk/blob/0156ca8f959d5cf3787c18113ce48acaaf1a8345/polkadot/xcm/src/lib.rs#L131). This would cause all calls with a lower version to fail. The correct function to use is the good old [try_into](https://github.com/paritytech/polkadot-sdk/blob/0156ca8f959d5cf3787c18113ce48acaaf1a8345/polkadot/xcm/src/lib.rs#L184). Now those calls work :) --------- Co-authored-by: command-bot <> Co-authored-by: Branislav Kontur <[email protected]> Co-authored-by: GitHub Action <[email protected]>
1 parent fc315ac commit 139691b

File tree

36 files changed

+483
-82
lines changed

36 files changed

+483
-82
lines changed

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ snowbridge-router-primitives = { workspace = true }
9999

100100
[dev-dependencies]
101101
asset-test-utils = { workspace = true, default-features = true }
102+
parachains-runtimes-test-utils = { workspace = true, default-features = true }
102103

103104
[build-dependencies]
104105
substrate-wasm-builder = { optional = true, workspace = true, default-features = true }

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,37 +1415,31 @@ impl_runtime_apis! {
14151415
// We accept the native token to pay fees.
14161416
let mut acceptable_assets = vec![AssetId(native_token.clone())];
14171417
// We also accept all assets in a pool with the native token.
1418-
let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::<
1419-
Runtime,
1420-
xcm::v5::Location
1421-
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
1422-
acceptable_assets.extend(assets_in_pool_with_native);
1418+
acceptable_assets.extend(
1419+
assets_common::PoolAdapter::<Runtime>::get_assets_in_pool_with(native_token)
1420+
.map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?
1421+
);
14231422
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
14241423
}
14251424

14261425
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
14271426
let native_asset = xcm_config::TokenLocation::get();
14281427
let fee_in_native = WeightToFee::weight_to_fee(&weight);
1429-
match asset.try_as::<AssetId>() {
1428+
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
1429+
match latest_asset_id {
14301430
Ok(asset_id) if asset_id.0 == native_asset => {
14311431
// for native token
14321432
Ok(fee_in_native)
14331433
},
14341434
Ok(asset_id) => {
1435-
let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::<
1436-
Runtime,
1437-
xcm::v5::Location
1438-
>(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?;
1439-
if assets_in_pool_with_this_asset
1440-
.into_iter()
1441-
.map(|asset_id| asset_id.0)
1442-
.any(|location| location == native_asset) {
1443-
pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
1444-
asset_id.clone().0,
1435+
// Try to get current price of `asset_id` in `native_asset`.
1436+
if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::<Runtime>::quote_price_tokens_for_exact_tokens(
1437+
asset_id.0.clone(),
14451438
native_asset,
14461439
fee_in_native,
14471440
true, // We include the fee.
1448-
).ok_or(XcmPaymentApiError::AssetNotFound)
1441+
) {
1442+
Ok(swapped_in_native)
14491443
} else {
14501444
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
14511445
Err(XcmPaymentApiError::AssetNotFound)

cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ use asset_hub_rococo_runtime::{
2424
ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, LocationToAccountId, StakingPot,
2525
TokenLocation, TrustBackedAssetsPalletLocation, XcmConfig,
2626
},
27-
AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection,
28-
ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase,
29-
MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
30-
SessionKeys, TrustBackedAssetsInstance, XcmpQueue,
27+
AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, Block,
28+
CollatorSelection, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance,
29+
MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall,
30+
RuntimeEvent, RuntimeOrigin, SessionKeys, TrustBackedAssetsInstance, XcmpQueue,
3131
};
3232
use asset_test_utils::{
3333
test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys,
@@ -1471,3 +1471,19 @@ fn location_conversion_works() {
14711471
assert_eq!(got, expected, "{}", tc.description);
14721472
}
14731473
}
1474+
1475+
#[test]
1476+
fn xcm_payment_api_works() {
1477+
parachains_runtimes_test_utils::test_cases::xcm_payment_api_with_native_token_works::<
1478+
Runtime,
1479+
RuntimeCall,
1480+
RuntimeOrigin,
1481+
Block,
1482+
>();
1483+
asset_test_utils::test_cases::xcm_payment_api_with_pools_works::<
1484+
Runtime,
1485+
RuntimeCall,
1486+
RuntimeOrigin,
1487+
Block,
1488+
>();
1489+
}

cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ snowbridge-router-primitives = { workspace = true }
101101

102102
[dev-dependencies]
103103
asset-test-utils = { workspace = true, default-features = true }
104+
parachains-runtimes-test-utils = { workspace = true, default-features = true }
104105

105106
[build-dependencies]
106107
substrate-wasm-builder = { optional = true, workspace = true, default-features = true }

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

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,38 +1528,31 @@ impl_runtime_apis! {
15281528
// We accept the native token to pay fees.
15291529
let mut acceptable_assets = vec![AssetId(native_token.clone())];
15301530
// We also accept all assets in a pool with the native token.
1531-
let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::<
1532-
Runtime,
1533-
xcm::v5::Location
1534-
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
1535-
acceptable_assets.extend(assets_in_pool_with_native);
1531+
acceptable_assets.extend(
1532+
assets_common::PoolAdapter::<Runtime>::get_assets_in_pool_with(native_token)
1533+
.map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?
1534+
);
15361535
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
15371536
}
15381537

15391538
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
15401539
let native_asset = xcm_config::WestendLocation::get();
15411540
let fee_in_native = WeightToFee::weight_to_fee(&weight);
1542-
match asset.try_as::<AssetId>() {
1541+
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
1542+
match latest_asset_id {
15431543
Ok(asset_id) if asset_id.0 == native_asset => {
15441544
// for native asset
15451545
Ok(fee_in_native)
15461546
},
15471547
Ok(asset_id) => {
1548-
// We recognize assets in a pool with the native one.
1549-
let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::<
1550-
Runtime,
1551-
xcm::v5::Location
1552-
>(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?;
1553-
if assets_in_pool_with_this_asset
1554-
.into_iter()
1555-
.map(|asset_id| asset_id.0)
1556-
.any(|location| location == native_asset) {
1557-
pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
1558-
asset_id.clone().0,
1548+
// Try to get current price of `asset_id` in `native_asset`.
1549+
if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::<Runtime>::quote_price_tokens_for_exact_tokens(
1550+
asset_id.0.clone(),
15591551
native_asset,
15601552
fee_in_native,
15611553
true, // We include the fee.
1562-
).ok_or(XcmPaymentApiError::AssetNotFound)
1554+
) {
1555+
Ok(swapped_in_native)
15631556
} else {
15641557
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
15651558
Err(XcmPaymentApiError::AssetNotFound)

cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use asset_hub_westend_runtime::{
2424
ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, LocationToAccountId, StakingPot,
2525
TrustBackedAssetsPalletLocation, WestendLocation, XcmConfig,
2626
},
27-
AllPalletsWithoutSystem, Assets, Balances, ExistentialDeposit, ForeignAssets,
27+
AllPalletsWithoutSystem, Assets, Balances, Block, ExistentialDeposit, ForeignAssets,
2828
ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem,
2929
PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys,
3030
TrustBackedAssetsInstance, XcmpQueue,
@@ -1446,3 +1446,19 @@ fn location_conversion_works() {
14461446
assert_eq!(got, expected, "{}", tc.description);
14471447
}
14481448
}
1449+
1450+
#[test]
1451+
fn xcm_payment_api_works() {
1452+
parachains_runtimes_test_utils::test_cases::xcm_payment_api_with_native_token_works::<
1453+
Runtime,
1454+
RuntimeCall,
1455+
RuntimeOrigin,
1456+
Block,
1457+
>();
1458+
asset_test_utils::test_cases::xcm_payment_api_with_pools_works::<
1459+
Runtime,
1460+
RuntimeCall,
1461+
RuntimeOrigin,
1462+
Block,
1463+
>();
1464+
}

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

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ extern crate alloc;
2828
use crate::matching::{LocalLocationPattern, ParentLocation};
2929
use alloc::vec::Vec;
3030
use codec::{Decode, EncodeLike};
31-
use core::cmp::PartialEq;
31+
use core::{cmp::PartialEq, marker::PhantomData};
3232
use frame_support::traits::{Equals, EverythingBut};
3333
use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId};
3434
use sp_runtime::traits::TryConvertInto;
@@ -137,24 +137,62 @@ pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> =
137137
TryConvertInto,
138138
>;
139139

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)| {
140+
/// Adapter implementation for accessing pools (`pallet_asset_conversion`) that uses `AssetKind` as
141+
/// a `xcm::v*` which could be different from the `xcm::latest`.
142+
pub struct PoolAdapter<Runtime>(PhantomData<Runtime>);
143+
impl<
144+
Runtime: pallet_asset_conversion::Config<PoolId = (L, L), AssetKind = L>,
145+
L: TryFrom<Location> + TryInto<Location> + Clone + Decode + EncodeLike + PartialEq,
146+
> PoolAdapter<Runtime>
147+
{
148+
/// Returns a vector of all assets in a pool with `asset`.
149+
///
150+
/// Should only be used in runtime APIs since it iterates over the whole
151+
/// `pallet_asset_conversion::Pools` map.
152+
///
153+
/// It takes in any version of an XCM Location but always returns the latest one.
154+
/// This is to allow some margin of migrating the pools when updating the XCM version.
155+
///
156+
/// An error of type `()` is returned if the version conversion fails for XCM locations.
157+
/// This error should be mapped by the caller to a more descriptive one.
158+
pub fn get_assets_in_pool_with(asset: Location) -> Result<Vec<AssetId>, ()> {
159+
// convert latest to the `L` version.
160+
let asset: L = asset.try_into().map_err(|_| ())?;
161+
Self::iter_assets_in_pool_with(&asset)
162+
.map(|location| {
163+
// convert `L` to the latest `AssetId`
164+
location.try_into().map_err(|_| ()).map(AssetId)
165+
})
166+
.collect::<Result<Vec<_>, _>>()
167+
}
168+
169+
/// Provides a current prices. Wrapper over
170+
/// `pallet_asset_conversion::Pallet::<T>::quote_price_tokens_for_exact_tokens`.
171+
///
172+
/// An error of type `()` is returned if the version conversion fails for XCM locations.
173+
/// This error should be mapped by the caller to a more descriptive one.
174+
pub fn quote_price_tokens_for_exact_tokens(
175+
asset_1: Location,
176+
asset_2: Location,
177+
amount: Runtime::Balance,
178+
include_fees: bool,
179+
) -> Result<Option<Runtime::Balance>, ()> {
180+
// Convert latest to the `L` version.
181+
let asset_1: L = asset_1.try_into().map_err(|_| ())?;
182+
let asset_2: L = asset_2.try_into().map_err(|_| ())?;
183+
184+
// Quote swap price.
185+
Ok(pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
186+
asset_1,
187+
asset_2,
188+
amount,
189+
include_fees,
190+
))
191+
}
192+
193+
/// Helper function for filtering pool.
194+
pub fn iter_assets_in_pool_with(asset: &L) -> impl Iterator<Item = L> + '_ {
195+
pallet_asset_conversion::Pools::<Runtime>::iter_keys().filter_map(|(asset_1, asset_2)| {
158196
if asset_1 == *asset {
159197
Some(asset_2)
160198
} else if asset_2 == *asset {
@@ -163,8 +201,7 @@ pub fn get_assets_in_pool_with<
163201
None
164202
}
165203
})
166-
.map(|location| location.try_into().map_err(|_| ()).map(AssetId))
167-
.collect::<Result<Vec<_>, _>>()
204+
}
168205
}
169206

170207
#[cfg(test)]

0 commit comments

Comments
 (0)