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

Assets in pool with native can be used in query_weight_to_asset_fee #6080

Merged
merged 22 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
17b080b
fix: assets in pool with native can be used to query for fees
franciscoaguirre Oct 15, 2024
375908a
chore: refactor pools helper function to assets-common
franciscoaguirre Oct 18, 2024
aec5b5b
test(asset-hub-westend-integration-tests): XcmPaymentApi
franciscoaguirre Oct 18, 2024
dcbb796
chore: move test to macro and use in both asset hubs
franciscoaguirre Oct 18, 2024
30cf711
chore: remove comment
franciscoaguirre Oct 18, 2024
a4e80ac
doc: add prdoc
franciscoaguirre Oct 18, 2024
e4e1ca0
Merge branch 'master' into query-weight-to-asset-fee-multiple-assets
franciscoaguirre Oct 18, 2024
9121790
".git/.scripts/commands/fmt/fmt.sh"
Oct 18, 2024
9b1f15b
fix: used invalid character in prdoc
franciscoaguirre Oct 18, 2024
932becb
Merge branch 'master' into query-weight-to-asset-fee-multiple-assets
franciscoaguirre Oct 18, 2024
86b9da8
Merge branch 'master' into query-weight-to-asset-fee-multiple-assets
franciscoaguirre Oct 21, 2024
860856b
chore(assets-common): make get_assets_in_pool_with more generic
franciscoaguirre Oct 21, 2024
4b01538
Merge branch 'master' into query-weight-to-asset-fee-multiple-assets
franciscoaguirre Oct 21, 2024
66897ac
Merge branch 'master' into query-weight-to-asset-fee-multiple-assets
franciscoaguirre Oct 22, 2024
385137b
chore(assets-common): make get_assets_in_pool_with more generic
franciscoaguirre Oct 22, 2024
2ff5cf7
Merge branch 'master' into query-weight-to-asset-fee-multiple-assets
franciscoaguirre Oct 22, 2024
db4040b
chore(assets-common): return a vec instead of an iterator in get_asse…
franciscoaguirre Oct 22, 2024
88d2f2d
fix(asset-hub-rococo): update get_assets_in_pool_with
franciscoaguirre Oct 22, 2024
14eb027
".git/.scripts/commands/fmt/fmt.sh"
Oct 22, 2024
e650905
fix: fmt
franciscoaguirre Oct 22, 2024
82ada8a
chore: get_asset_in_pool_with returns asset id instead of location
franciscoaguirre Oct 22, 2024
010299d
chore: any() instead of find().is_some()
franciscoaguirre Oct 22, 2024
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
68 changes: 68 additions & 0 deletions cumulus/parachains/integration-tests/emulated/common/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,71 @@ macro_rules! test_dry_run_transfer_across_pk_bridge {
}
};
}

#[macro_export]
macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub {
( $asset_hub:ty ) => {
$crate::macros::paste::paste! {
use emulated_integration_tests_common::USDT_ID;
use xcm_runtime_apis::fees::{Error as XcmPaymentApiError, runtime_decl_for_xcm_payment_api::XcmPaymentApiV1};

$asset_hub::execute_with(|| {
// Setup a pool between USDT and WND.
type RuntimeOrigin = <$asset_hub as Chain>::RuntimeOrigin;
type Assets = <$asset_hub as [<$asset_hub Pallet>]>::Assets;
type AssetConversion = <$asset_hub as [<$asset_hub Pallet>]>::AssetConversion;
let wnd = Location::new(1, []);
let usdt = Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
let sender = [<$asset_hub Sender>]::get();
assert_ok!(AssetConversion::create_pool(
RuntimeOrigin::signed(sender.clone()),
Box::new(wnd.clone()),
Box::new(usdt.clone()),
));

type Runtime = <$asset_hub as Chain>::Runtime;
let acceptable_payment_assets = Runtime::query_acceptable_payment_assets(4).unwrap();
assert_eq!(acceptable_payment_assets, vec![
VersionedAssetId::from(AssetId(wnd.clone())),
VersionedAssetId::from(AssetId(usdt.clone())),
]);

let program = Xcm::<()>::builder()
.withdraw_asset((Parent, 100u128))
.buy_execution((Parent, 10u128), Unlimited)
.deposit_asset(All, [0u8; 32])
.build();
let weight = Runtime::query_xcm_weight(VersionedXcm::from(program)).unwrap();
let fee_in_wnd = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(wnd.clone()))).unwrap();
// Assets not in a pool don't work.
assert!(Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1)])))).is_err());
let fee_in_usdt_fail = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt.clone())));
// Weight to asset fee fails because there's not enough asset in the pool.
// We just created it, there's none.
assert_eq!(fee_in_usdt_fail, Err(XcmPaymentApiError::AssetNotFound));
// We add some.
assert_ok!(Assets::mint(
RuntimeOrigin::signed(sender.clone()),
USDT_ID.into(),
sender.clone().into(),
5_000_000_000_000
));
// We make 1 WND = 4 USDT.
assert_ok!(AssetConversion::add_liquidity(
RuntimeOrigin::signed(sender.clone()),
Box::new(wnd),
Box::new(usdt.clone()),
1_000_000_000_000,
4_000_000_000_000,
0,
0,
sender.into()
));
// Now it works.
let fee_in_usdt = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt)));
assert_ok!(fee_in_usdt);
assert!(fee_in_usdt.unwrap() > fee_in_wnd);
});
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ mod imports {
pub use emulated_integration_tests_common::{
accounts::DUMMY_EMPTY,
test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay,
test_relay_is_trusted_teleporter,
test_relay_is_trusted_teleporter, test_xcm_fee_querying_apis_work_for_asset_hub,
xcm_emulator::{
assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test,
TestArgs, TestContext, TestExt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() {
);
});
}

#[test]
fn xcm_fee_querying_apis_work() {
test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubRococo);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod imports {
pub use emulated_integration_tests_common::{
accounts::DUMMY_EMPTY,
test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay,
test_relay_is_trusted_teleporter,
test_relay_is_trusted_teleporter, test_xcm_fee_querying_apis_work_for_asset_hub,
xcm_emulator::{
assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test,
TestArgs, TestContext, TestExt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() {
);
});
}

#[test]
fn xcm_fee_querying_apis_work() {
test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubWestend);
}
44 changes: 27 additions & 17 deletions cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1412,31 +1412,41 @@ 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.
acceptable_assets.extend(
pallet_asset_conversion::Pools::<Runtime>::iter_keys().filter_map(
|(asset_1, asset_2)| {
if asset_1 == native_token {
Some(asset_2.clone().into())
} else if asset_2 == native_token {
Some(asset_1.clone().into())
} else {
None
}
},
),
);
let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::<
Runtime,
xcm::v4::Location
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?;

acceptable_assets.extend(assets_in_pool_with_native);
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
let native_asset = xcm_config::TokenLocation::get();
let fee_in_native = WeightToFee::weight_to_fee(&weight);
match asset.try_as::<AssetId>() {
Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => {
Ok(asset_id) if asset_id.0 == native_asset => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
Ok(fee_in_native)
},
Ok(asset_id) => {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
Err(XcmPaymentApiError::AssetNotFound)
let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::<
Runtime,
xcm::v4::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) {
Comment on lines +1437 to +1439
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this compiles like this or maybe &native_asset

Suggested change
.into_iter()
.map(|asset_id| asset_id.0)
.any(|location| location == native_asset) {
.iter()
.any(|asset_id| asset_id.0 == native_asset) {

pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
asset_id.clone().0,
native_asset,
fee_in_native,
true, // We include the fee.
).ok_or(XcmPaymentApiError::AssetNotFound)
} 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:?}!");
Expand Down
47 changes: 29 additions & 18 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1450,31 +1450,42 @@ 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.
acceptable_assets.extend(
pallet_asset_conversion::Pools::<Runtime>::iter_keys().filter_map(
|(asset_1, asset_2)| {
if asset_1 == native_token {
Some(asset_2.clone().into())
} else if asset_2 == native_token {
Some(asset_1.clone().into())
} else {
None
}
},
),
);
let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::<
Runtime,
xcm::v4::Location
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think .into_iter() is not needed now

Suggested change
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter();
>(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?;

acceptable_assets.extend(assets_in_pool_with_native);
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
}

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);
match asset.try_as::<AssetId>() {
Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
Ok(asset_id) if asset_id.0 == native_asset => {
// for native asset
Ok(fee_in_native)
},
Ok(asset_id) => {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
Err(XcmPaymentApiError::AssetNotFound)
// 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::v4::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) {
Comment on lines +1476 to +1478
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.into_iter()
.map(|asset_id| asset_id.0)
.any(|location| location == native_asset) {
.iter()
.any(|asset_id| asset_id.0 == native_asset) {

pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
asset_id.clone().0,
native_asset,
fee_in_native,
true, // We include the fee.
).ok_or(XcmPaymentApiError::AssetNotFound)
} 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:?}!");
Expand Down
33 changes: 33 additions & 0 deletions cumulus/parachains/runtimes/assets/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub mod runtime_api;
extern crate alloc;

use crate::matching::{LocalLocationPattern, ParentLocation};
use alloc::vec::Vec;
use codec::{Decode, EncodeLike};
use core::cmp::PartialEq;
use frame_support::traits::{Equals, EverythingBut};
use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId};
use sp_runtime::traits::TryConvertInto;
Expand Down Expand Up @@ -134,6 +137,36 @@ 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)| {
if asset_1 == *asset {
Some(asset_2)
} else if asset_2 == *asset {
Some(asset_1)
} else {
None
}
})
.map(|location| location.try_into().map_err(|_| ()).map(AssetId))
.collect::<Result<Vec<_>, _>>()
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
22 changes: 22 additions & 0 deletions prdoc/pr_6080.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: Assets in pool with native can be used in query_weight_to_asset_fee in Asset Hubs

doc:
- audience: Runtime User
description: |
`query_weight_to_asset_fee` now works with assets in a pool with the native asset in both
Westend and Rococo asset hubs.
This means all the information you get from `query_acceptable_payment_assets` can be used
directly in `query_weight_to_asset_fee` to get the correct fees that need to be paid.

crates:
- name: assets-common
bump: minor
- name: asset-hub-westend-runtime
bump: minor
- name: asset-hub-rococo-runtime
bump: minor
- name: emulated-integration-tests-common
bump: minor
Loading