Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify XcmPaymentApi with PoolAdapter #6467

Merged
Show file tree
Hide file tree
Changes from 3 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
25 changes: 9 additions & 16 deletions cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1412,11 +1412,10 @@ impl_runtime_apis! {
// We accept the native token to pay fees.
let mut acceptable_assets = vec![AssetId(native_token.clone())];
// We also accept all assets in a pool with the native token.
let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::<
Runtime,
xcm::v5::Location
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
acceptable_assets.extend(assets_in_pool_with_native);
acceptable_assets.extend(
assets_common::PoolAdapter::<Runtime>::get_assets_in_pool_with(native_token)
.map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?
);
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
}

Expand All @@ -1430,20 +1429,14 @@ impl_runtime_apis! {
Ok(fee_in_native)
},
Ok(asset_id) => {
let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::<
Runtime,
xcm::v5::Location
>(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?;
if assets_in_pool_with_this_asset
.into_iter()
.map(|asset_id| asset_id.0)
.any(|location| location == native_asset) {
pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
asset_id.clone().0,
// 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_or(XcmPaymentApiError::AssetNotFound)
) {
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)
Expand Down
26 changes: 9 additions & 17 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1528,11 +1528,10 @@ impl_runtime_apis! {
// We accept the native token to pay fees.
let mut acceptable_assets = vec![AssetId(native_token.clone())];
// We also accept all assets in a pool with the native token.
let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::<
Runtime,
xcm::v5::Location
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
acceptable_assets.extend(assets_in_pool_with_native);
acceptable_assets.extend(
assets_common::PoolAdapter::<Runtime>::get_assets_in_pool_with(native_token)
.map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?
);
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
}

Expand All @@ -1546,21 +1545,14 @@ impl_runtime_apis! {
Ok(fee_in_native)
},
Ok(asset_id) => {
// We recognize assets in a pool with the native one.
let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::<
Runtime,
xcm::v5::Location
>(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?;
if assets_in_pool_with_this_asset
.into_iter()
.map(|asset_id| asset_id.0)
.any(|location| location == native_asset) {
pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
asset_id.clone().0,
// 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_or(XcmPaymentApiError::AssetNotFound)
) {
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)
Expand Down
84 changes: 63 additions & 21 deletions cumulus/parachains/runtimes/assets/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extern crate alloc;
use crate::matching::{LocalLocationPattern, ParentLocation};
use alloc::vec::Vec;
use codec::{Decode, EncodeLike};
use core::cmp::PartialEq;
use core::{cmp::PartialEq, marker::PhantomData};
use frame_support::traits::{Equals, EverythingBut};
use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId};
use sp_runtime::traits::TryConvertInto;
Expand Down Expand Up @@ -137,24 +137,67 @@ pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> =
TryConvertInto,
>;

/// Returns an iterator of all assets in a pool with `asset`.
///
/// Should only be used in runtime APIs since it iterates over the whole
/// `pallet_asset_conversion::Pools` map.
///
/// It takes in any version of an XCM Location but always returns the latest one.
/// This is to allow some margin of migrating the pools when updating the XCM version.
///
/// An error of type `()` is returned if the version conversion fails for XCM locations.
/// This error should be mapped by the caller to a more descriptive one.
pub fn get_assets_in_pool_with<
Runtime: pallet_asset_conversion::Config<PoolId = (L, L)>,
L: TryInto<Location> + Clone + Decode + EncodeLike + PartialEq,
>(
asset: &L,
) -> Result<Vec<AssetId>, ()> {
pallet_asset_conversion::Pools::<Runtime>::iter_keys()
.filter_map(|(asset_1, asset_2)| {
/// Adapter implementation for accessing pools (`pallet_asset_conversion`) that uses `AssetKind` as
/// a `xcm::v*` which could be different from the `xcm::latest`.
pub struct PoolAdapter<Runtime>(PhantomData<Runtime>);
impl<
Runtime: pallet_asset_conversion::Config<PoolId = (L, L), AssetKind = L>,
L: TryFrom<Location> + TryInto<Location> + Clone + Decode + EncodeLike + PartialEq,
> PoolAdapter<Runtime>
{
/// Returns a vector of all assets in a pool with `asset`.
///
/// Should only be used in runtime APIs since it iterates over the whole
/// `pallet_asset_conversion::Pools` map.
///
/// It takes in any version of an XCM Location but always returns the latest one.
/// This is to allow some margin of migrating the pools when updating the XCM version.
///
/// An error of type `()` is returned if the version conversion fails for XCM locations.
/// This error should be mapped by the caller to a more descriptive one.
pub fn get_assets_in_pool_with(asset: Location) -> Result<Vec<AssetId>, ()> {
// convert latest to the `L` version.
let asset: L = asset.try_into().map_err(|_| ())?;
Self::iter_assets_in_pool_with(&asset)
.map(|location| {
// convert `L` to the latest `AssetId`
location.try_into().map_err(|_| ()).map(AssetId)
})
.collect::<Result<Vec<_>, _>>()
}

/// Provides a current prices. Wrapper over
/// `pallet_asset_conversion::Pallet::<T>::quote_price_tokens_for_exact_tokens`.
///
/// An error of type `()` is returned if the version conversion fails for XCM locations.
/// This error should be mapped by the caller to a more descriptive one.
pub fn quote_price_tokens_for_exact_tokens(
asset_1: Location,
asset_2: Location,
amount: Runtime::Balance,
include_fees: bool,
) -> Result<Option<Runtime::Balance>, ()> {
// convert latest to the `L` version.
let asset_1: L = asset_1.try_into().map_err(|_| ())?;
let asset_2: L = asset_2.try_into().map_err(|_| ())?;

// check if we have a pool for `(asset_1, asset_2)`.
if !Self::iter_assets_in_pool_with(&asset_1).any(|a| a == asset_2) {
bkontur marked this conversation as resolved.
Show resolved Hide resolved
return Ok(None)
}

// if so, let's do a conversion.
Ok(pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
asset_1,
asset_2,
amount,
include_fees,
))
}

/// Helper function for filtering pool.
pub fn iter_assets_in_pool_with(asset: &L) -> impl Iterator<Item = L> + '_ {
pallet_asset_conversion::Pools::<Runtime>::iter_keys().filter_map(|(asset_1, asset_2)| {
if asset_1 == *asset {
Some(asset_2)
} else if asset_2 == *asset {
Expand All @@ -163,8 +206,7 @@ pub fn get_assets_in_pool_with<
None
}
})
.map(|location| location.try_into().map_err(|_| ()).map(AssetId))
.collect::<Result<Vec<_>, _>>()
}
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions prdoc/pr_6459.prdoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ crates:
bump: patch
- name: xcm-runtime-apis
bump: patch
- name: assets-common
bump: patch
Loading